Changes in / [42f6e07:2b4daf2]
- Files:
-
- 57 added
- 11 deleted
- 87 edited
Legend:
- Unmodified
- Added
- Removed
-
Jenkins/FullBuild
r42f6e07 r2b4daf2 32 32 33 33 stage('Package') { 34 build job: 'Cforall_Distribute_Ref', parameters: [string(name: 'GitRef', value: commitId), string(name: 'Build', value: currentBuild.number.toString())]34 trigger_dist( commitId, currentBuild.number.toString() ) 35 35 } 36 36 } … … 103 103 } 104 104 105 def trigger_dist(String commitId, String buildNum) { 106 def result = build job: 'Cforall_Distribute_Ref', \ 107 parameters: [ \ 108 string(name: 'GitRef', value: commitId), \ 109 string(name: 'Build' , value: buildNum) \ 110 ], \ 111 propagate: false 112 113 echo(result.result) 114 115 if(result.result != 'SUCCESS') { 116 sh("wget -q -O - https://cforall.uwaterloo.ca/jenkins/job/Cforall_Distribute_Ref/${result.number}/consoleText") 117 error(result.result) 118 } 119 } 120 105 121 //=========================================================================================================== 106 122 //Routine responsible of sending the email notification once the build is completed -
Jenkins/tools.groovy
r42f6e07 r2b4daf2 6 6 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils 7 7 8 // Global for the stage name9 StageName = ''10 11 8 // wrapper around stage declaretion to be more verbose 12 9 // and allow showing as skipped in the UI 13 10 def BuildStage(String name, boolean run, Closure block ) { 14 StageName = name 15 echo " -------- ${StageName} -------- " 11 echo " -------- ${name} -------- " 16 12 if(run) { 17 13 stage(name, block) -
Jenkinsfile
r42f6e07 r2b4daf2 54 54 //attach the build log to the email 55 55 catch (Exception caughtError) { 56 //rethrow error later 56 // Store the result of the build log 57 currentBuild.result = "FAILURE" 58 59 // An error has occured, the build log is relevent 60 log_needed = true 61 62 // rethrow error later 57 63 err = caughtError 58 64 65 // print the error so it shows in the log 59 66 echo err.toString() 60 61 //An error has occured, the build log is relevent62 log_needed = true63 64 //Store the result of the build log65 currentBuild.result = "${tools.StageName} FAILURE".trim()66 67 } 67 68 -
benchmark/Makefile.am
r42f6e07 r2b4daf2 522 522 size-cfa$(EXEEXT): 523 523 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/size/size.cfa 524 525 ## ========================================================================================================= 526 527 %-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs 528 cd $(builddir) && cargo build --release 529 cp $(builddir)/target/release/$(basename $@) $@ -
benchmark/creation/node_cor.js
r42f6e07 r2b4daf2 6 6 function * coroutine() { yield } 7 7 8 for ( var i = 0; i < times; i += 1 ) { // warm jit8 for ( var i = 0; i < times; i += 1 ) { // warm JIT 9 9 cor = coroutine() 10 10 } -
benchmark/ctxswitch/node_cor.js
r42f6e07 r2b4daf2 11 11 cor = coroutine() 12 12 13 for ( var i = 0; i < times; i += 1 ) { // warm git13 for ( var i = 0; i < times; i += 1 ) { // warm JIT 14 14 cor.next(); 15 15 } -
benchmark/readyQ/bench.go
r42f6e07 r2b4daf2 5 5 "flag" 6 6 "fmt" 7 "log" 7 8 "os" 8 9 "runtime" 10 "runtime/pprof" 9 11 "sync/atomic" 10 12 "time" … … 43 45 } 44 46 45 func bench_init() {47 func bench_init() func() { 46 48 nprocsOpt := flag.Int("p", 1, "The number of processors") 47 49 nthreadsOpt := flag.Int("t", 1, "The number of threads") 48 50 durationOpt := flag.Float64("d", 0, "Duration of the experiment in seconds") 49 51 stopOpt := flag.Uint64("i", 0, "Duration of the experiment in iterations") 52 cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") 50 53 51 54 flag.Parse() … … 72 75 73 76 runtime.GOMAXPROCS(nprocs) 77 78 if (*cpuprofile) != "" { 79 f, err := os.Create(*cpuprofile) 80 if err != nil { 81 log.Fatal(err) 82 } 83 pprof.StartCPUProfile(f) 84 } 85 86 return func() { 87 if (*cpuprofile) != "" { 88 pprof.StopCPUProfile() 89 } 90 } 74 91 } -
benchmark/readyQ/cycle.cpp
r42f6e07 r2b4daf2 71 71 { 'r', "ringsize", "Number of threads in a cycle", ring_size } 72 72 }; 73 BENCH_OPT_PARSE(" cforallcycle benchmark");73 BENCH_OPT_PARSE("libfibre cycle benchmark"); 74 74 75 75 { -
benchmark/readyQ/cycle.rs
r42f6e07 r2b4daf2 1 #[cfg(any(2 feature = "sync time rt-threaded",3 ))]4 5 extern crate tokio;6 7 use std::io::{self, Write};8 1 use std::sync::Arc; 9 use std::sync::atomic:: {AtomicU64, AtomicBool,Ordering};10 use std::time:: {Instant,Duration};2 use std::sync::atomic::Ordering; 3 use std::time::Instant; 11 4 12 5 use tokio::runtime::Builder; 13 6 use tokio::sync; 14 use tokio::time;15 7 16 extern crate isatty; 17 use isatty::stdout_isatty; 18 19 extern crate num_format; 8 use clap::{Arg, App}; 20 9 use num_format::{Locale, ToFormattedString}; 21 10 22 extern crate clap; 23 use clap::{Arg, App};11 #[path = "../bench.rs"] 12 mod bench; 24 13 25 use std::cell::UnsafeCell; 26 use std::mem::MaybeUninit; 27 use std::ops; 28 29 pub struct InitializeCell<T> { 30 inner: UnsafeCell<MaybeUninit<T>>, 31 } 32 33 unsafe impl<T> Sync for InitializeCell<T> {} 34 35 impl<T> InitializeCell<T> { 36 pub const unsafe fn new_uninitialized() -> InitializeCell<T> { 37 InitializeCell { 38 inner: UnsafeCell::new(MaybeUninit::uninit()), 39 } 40 } 41 pub const fn new(init: T) -> InitializeCell<T> { 42 InitializeCell { 43 inner: UnsafeCell::new(MaybeUninit::new(init)), 44 } 45 } 46 pub unsafe fn init(&self, init: T) { 47 (*self.inner.get()) = MaybeUninit::new(init); 48 } 49 } 50 51 impl<T> ops::Deref for InitializeCell<T> { 52 type Target = T; 53 fn deref(&self) -> &T { 54 unsafe { 55 &*(*self.inner.get()).as_ptr() 56 } 57 } 58 } 59 60 static CLOCK_MODE: InitializeCell<bool> = unsafe { InitializeCell::new_uninitialized() }; 61 static STOP_COUNT: InitializeCell<u64> = unsafe { InitializeCell::new_uninitialized() }; 62 static DURATION: InitializeCell<f64> = unsafe { InitializeCell::new_uninitialized() }; 63 static STOP : AtomicBool = AtomicBool::new(false); 64 static THREADS_LEFT : AtomicU64 = AtomicU64 ::new(10); 65 14 // ================================================== 66 15 struct Partner { 67 16 sem: sync::Semaphore, … … 69 18 } 70 19 71 async fn partner_main( result: sync::oneshot::Sender<u64>, idx: usize, others: Arc<Vec<Arc<Partner>>> ){20 async fn partner_main(idx: usize, others: Arc<Vec<Arc<Partner>>>, exp: Arc<bench::BenchData> ) -> u64 { 72 21 let this = &others[idx]; 73 22 let mut count:u64 = 0; … … 77 26 count += 1; 78 27 79 if *CLOCK_MODE && STOP.load(Ordering::Relaxed) { break; }80 if ! *CLOCK_MODE && count >= *STOP_COUNT{ break; }28 if exp.clock_mode && exp.stop.load(Ordering::Relaxed) { break; } 29 if !exp.clock_mode && count >= exp.stop_count { break; } 81 30 } 82 31 83 THREADS_LEFT.fetch_sub(1, Ordering::SeqCst);84 result.send( count ).unwrap();32 exp.threads_left.fetch_sub(1, Ordering::SeqCst); 33 count 85 34 } 86 35 87 fn prep(nthreads: usize, tthreads: usize) -> Vec<Arc<Partner>> { 88 let mut thddata = Vec::with_capacity(tthreads); 89 for i in 0..tthreads { 90 let pi = (i + nthreads) % tthreads; 91 thddata.push(Arc::new(Partner{ 92 sem: sync::Semaphore::new(0), 93 next: pi, 94 })); 95 } 96 return thddata; 97 } 98 99 async fn wait(start: &Instant, is_tty: bool) { 100 loop { 101 time::sleep(Duration::from_micros(100000)).await; 102 let delta = start.elapsed(); 103 if is_tty { 104 print!(" {:.1}\r", delta.as_secs_f32()); 105 io::stdout().flush().unwrap(); 106 } 107 if *CLOCK_MODE && delta >= Duration::from_secs_f64(*DURATION) { 108 break; 109 } 110 else if !*CLOCK_MODE && THREADS_LEFT.load(Ordering::Relaxed) == 0 { 111 break; 112 } 113 } 114 } 115 36 // ================================================== 116 37 fn main() { 117 38 let options = App::new("Cycle Tokio") 118 .arg(Arg::with_name("duration") .short("d").long("duration") .takes_value(true).default_value("5").help("Duration of the experiments in seconds")) 119 .arg(Arg::with_name("iterations").short("i").long("iterations").takes_value(true).conflicts_with("duration").help("Number of iterations of the experiments")) 120 .arg(Arg::with_name("nthreads") .short("t").long("nthreads") .takes_value(true).default_value("1").help("Number of threads to use")) 121 .arg(Arg::with_name("nprocs") .short("p").long("nprocs") .takes_value(true).default_value("1").help("Number of processors to use")) 39 .args(&bench::args()) 122 40 .arg(Arg::with_name("ringsize") .short("r").long("ringsize") .takes_value(true).default_value("1").help("Number of threads in a cycle")) 123 41 .get_matches(); … … 127 45 let nprocs = options.value_of("nprocs").unwrap().parse::<usize>().unwrap(); 128 46 129 if options.is_present("iterations") { 130 unsafe{ 131 CLOCK_MODE.init( false ); 132 STOP_COUNT.init( options.value_of("iterations").unwrap().parse::<u64>().unwrap() ); 133 } 134 } 135 else { 136 unsafe{ 137 CLOCK_MODE.init(true); 138 DURATION .init(options.value_of("duration").unwrap().parse::<f64>().unwrap()); 139 } 140 } 47 let tthreads = nthreads * ring_size; 48 let exp = Arc::new(bench::BenchData::new(options, tthreads)); 141 49 142 50 let s = (1000000 as u64).to_formatted_string(&Locale::en); 143 51 assert_eq!(&s, "1,000,000"); 144 52 145 146 let tthreads = nthreads * ring_size; 147 THREADS_LEFT.store(tthreads as u64, Ordering::SeqCst); 148 let thddata = Arc::new(prep(nthreads, tthreads)); 53 let thddata : Arc<Vec<Arc<Partner>>> = Arc::new( 54 (0..tthreads).map(|i| { 55 let pi = (i + nthreads) % tthreads; 56 Arc::new(Partner{ 57 sem: sync::Semaphore::new(0), 58 next: pi, 59 }) 60 }).collect() 61 ); 149 62 150 63 let mut global_counter :u64 = 0; … … 157 70 158 71 runtime.block_on(async { 159 let mut result : Vec<sync::oneshot::Receiver::<u64>> = Vec::with_capacity(tthreads); 160 { 161 let mut threads = Vec::with_capacity(tthreads); 162 for i in 0..tthreads { 163 let (s, r) = sync::oneshot::channel::<u64>(); 164 result.push(r); 165 threads.push(tokio::spawn(partner_main(s, i, thddata.clone()))); 166 } 167 println!("Starting"); 72 let threads: Vec<_> = (0..tthreads).map(|i| { 73 tokio::spawn(partner_main(i, thddata.clone(), exp.clone())) 74 }).collect(); 75 println!("Starting"); 168 76 169 let is_tty = stdout_isatty(); 170 let start = Instant::now(); 77 let start = Instant::now(); 171 78 172 173 174 79 for i in 0..nthreads { 80 thddata[i].sem.add_permits(1); 81 } 175 82 176 wait(&start, is_tty).await;83 duration = exp.wait(&start).await; 177 84 178 STOP.store(true, Ordering::SeqCst); 179 duration = start.elapsed(); 85 println!("\nDone"); 180 86 181 println!("\nDone"); 87 for i in 0..tthreads { 88 thddata[i].sem.add_permits(1); 89 } 182 90 183 for i in 0..tthreads { 184 thddata[i].sem.add_permits(1); 185 } 186 187 for _ in 0..tthreads { 188 global_counter += result.pop().unwrap().await.unwrap(); 189 } 91 for t in threads { 92 global_counter += t.await.unwrap(); 190 93 } 191 94 }); -
benchmark/readyQ/locality.go
r42f6e07 r2b4daf2 2 2 3 3 import ( 4 "context" 4 5 "flag" 5 6 "fmt" 6 7 "math/rand" 7 8 "os" 9 "syscall" 8 10 "sync/atomic" 9 11 "time" 12 "unsafe" 13 "golang.org/x/sync/semaphore" 10 14 "golang.org/x/text/language" 11 15 "golang.org/x/text/message" 12 16 ) 13 17 14 func handshake(stop chan struct {}, c chan [] uint64, data [] uint64, share bool) (bool, [] uint64) { 15 var s [] uint64 = data 16 if !share { 17 s = nil 18 } 19 20 // send the data 21 select { 22 case <- stop: 23 return true, nil 24 case c <- s: 25 } 26 27 // get the new data chunk 28 select { 29 case <- stop: 30 return true, nil 31 case n := <- c: 32 if share { 33 return false, n 34 } 35 return false, data 36 } 37 } 38 39 func local(result chan uint64, start chan struct{}, stop chan struct{}, size uint64, cnt uint64, channels []chan [] uint64, chan_cnt uint64, share bool) { 18 // ================================================== 19 type MyData struct { 20 _p1 [16]uint64 // padding 21 ttid int 22 id int 23 data [] uint64 24 _p2 [16]uint64 // padding 25 } 26 27 func NewData(id int, size uint64) (*MyData) { 40 28 var data [] uint64 41 29 data = make([]uint64, size) … … 43 31 data[i] = 0 44 32 } 45 count := uint64(0) 33 return &MyData{[16]uint64{0}, syscall.Gettid(), id, data,[16]uint64{0}} 34 } 35 36 func (this * MyData) moved( ttid int ) (uint64) { 37 if this.ttid == ttid { 38 return 0 39 } 40 this.ttid = ttid 41 return 1 42 } 43 44 func (this * MyData) access( idx uint64 ) { 45 this.data[idx % uint64(len(this.data))] += 1 46 } 47 48 // ================================================== 49 type MyCtx struct { 50 _p1 [16]uint64 // padding 51 s * semaphore.Weighted 52 d unsafe.Pointer 53 c context.Context 54 ttid int 55 id int 56 _p2 [16]uint64 // padding 57 } 58 59 func NewCtx( data * MyData, id int ) (MyCtx) { 60 r := MyCtx{[16]uint64{0},semaphore.NewWeighted(1), unsafe.Pointer(data), context.Background(), syscall.Gettid(), id,[16]uint64{0}} 61 r.s.Acquire(context.Background(), 1) 62 return r 63 } 64 65 func (this * MyCtx) moved( ttid int ) (uint64) { 66 if this.ttid == ttid { 67 return 0 68 } 69 this.ttid = ttid 70 return 1 71 } 72 73 // ================================================== 74 // Atomic object where a single thread can wait 75 // May exchanges data 76 type Spot struct { 77 _p1 [16]uint64 // padding 78 ptr uintptr // atomic variable use fo MES 79 id int // id for debugging 80 _p2 [16]uint64 // padding 81 } 82 83 // Main handshake of the code 84 // Single seat, first thread arriving waits 85 // Next threads unblocks current one and blocks in its place 86 // if share == true, exchange data in the process 87 func (this * Spot) put( ctx * MyCtx, data * MyData, share bool) (* MyData, bool) { 88 new := uintptr(unsafe.Pointer(ctx)) 89 // old_d := ctx.d 90 91 // Attempt to CAS our context into the seat 92 var raw uintptr 93 for true { 94 raw = this.ptr 95 if raw == uintptr(1) { // Seat is closed, return 96 return nil, true 97 } 98 if atomic.CompareAndSwapUintptr(&this.ptr, raw, new) { 99 break // We got the seat 100 } 101 } 102 103 // If we aren't the fist in, wake someone 104 if raw != uintptr(0) { 105 var val *MyCtx 106 val = (*MyCtx)(unsafe.Pointer(raw)) 107 108 // If we are sharing, give them our data 109 if share { 110 // fmt.Printf("[%d] - %d update %d: %p -> %p\n", this.id, ctx.id, val.id, val.d, data) 111 atomic.StorePointer(&val.d, unsafe.Pointer(data)) 112 } 113 114 // Wake them up 115 // fmt.Printf("[%d] - %d release %d\n", this.id, ctx.id, val.id) 116 val.s.Release(1) 117 } 118 119 // fmt.Printf("[%d] - %d enter\n", this.id, ctx.id) 120 121 // Block once on the seat 122 ctx.s.Acquire(ctx.c, 1) 123 124 // Someone woke us up, get the new data 125 ret := (* MyData)(atomic.LoadPointer(&ctx.d)) 126 // fmt.Printf("[%d] - %d leave: %p -> %p\n", this.id, ctx.id, ret, old_d) 127 128 return ret, false 129 } 130 131 // Shutdown the spot 132 // Wake current thread and mark seat as closed 133 func (this * Spot) release() { 134 val := (*MyCtx)(unsafe.Pointer(atomic.SwapUintptr(&this.ptr, uintptr(1)))) 135 if val == nil { 136 return 137 } 138 139 // Someone was there, release them 140 val.s.Release(1) 141 } 142 143 // ================================================== 144 // Struct for result, Go doesn't support passing tuple in channels 145 type Result struct { 146 count uint64 147 gmigs uint64 148 dmigs uint64 149 } 150 151 func NewResult() (Result) { 152 return Result{0, 0, 0} 153 } 154 155 // ================================================== 156 // Random number generator, Go's native one is to slow and global 157 func __xorshift64( state * uint64 ) (uint64) { 158 x := *state 159 x ^= x << 13 160 x ^= x >> 7 161 x ^= x << 17 162 *state = x 163 return x 164 } 165 166 // ================================================== 167 // Do some work by accessing 'cnt' cells in the array 168 func work(data * MyData, cnt uint64, state * uint64) { 169 for i := uint64(0); i < cnt; i++ { 170 data.access(__xorshift64(state)) 171 } 172 } 173 174 // Main body of the threads 175 func local(result chan Result, start chan struct{}, size uint64, cnt uint64, channels [] Spot, share bool, id int) { 176 // Initialize some data 177 state := rand.Uint64() // RNG state 178 data := NewData(id, size) // Starting piece of data 179 ctx := NewCtx(data, id) // Goroutine local context 180 181 // Prepare results 182 r := NewResult() 183 184 // Wait for start 46 185 <- start 186 187 // Main loop 47 188 for true { 48 for i := uint64(0); i < cnt; i++ {49 data[rand.Uint64() % size] += 150 } 51 52 i := rand.Uint64() % chan_cnt189 // Touch our current data, write to invalidate remote cache lines 190 work(data, cnt, &state) 191 192 // Wait on a random spot 193 i := __xorshift64(&state) % uint64(len(channels)) 53 194 var closed bool 54 closed, data = handshake(stop, channels[i], data, share) 55 count += 1 56 57 if closed { break } 58 if !clock_mode && count >= stop_count { break } 59 } 60 195 data, closed = channels[i].put(&ctx, data, share) 196 197 // Check if the experiment is over 198 if closed { break } // yes, spot was closed 199 if clock_mode && atomic.LoadInt32(&stop) == 1 { break } // yes, time's up 200 if !clock_mode && r.count >= stop_count { break } // yes, iterations reached 201 202 // Check everything is consistent 203 if uint64(len(data.data)) != size { panic("Data has weird size") } 204 205 // write down progress and check migrations 206 ttid := syscall.Gettid() 207 r.count += 1 208 r.gmigs += ctx .moved(ttid) 209 r.dmigs += data.moved(ttid) 210 } 211 212 // Mark goroutine as done 61 213 atomic.AddInt64(&threads_left, -1); 62 result <- count 63 } 64 214 215 // return result 216 result <- r 217 } 218 219 // ================================================== 220 // Program main 65 221 func main() { 66 67 work_sizeOpt := flag.Uint64("w", 2 , "Number of words (uint64) per threads") 68 countOpt := flag.Uint64("c", 2 , "Number of words (uint64) to touch") 222 // Benchmark specific command line arguments 223 nspotsOpt := flag.Int ("n", 0 , "Number of spots where threads sleep (nthreads - nspots are active at the same time)") 224 work_sizeOpt := flag.Uint64("w", 2 , "Size of the array for each threads, in words (64bit)") 225 countOpt := flag.Uint64("c", 2 , "Number of words to touch when working (random pick, cells can be picked more than once)") 69 226 shareOpt := flag.Bool ("s", false, "Pass the work data to the next thread when blocking") 70 227 71 bench_init() 72 228 // General benchmark initialization and deinitialization 229 defer bench_init()() 230 231 // Eval command line arguments 232 nspots:= *nspotsOpt 73 233 size := *work_sizeOpt 74 234 cnt := *countOpt 75 235 share := *shareOpt 76 236 237 if nspots == 0 { nspots = nthreads - nprocs; } 238 239 // Check params 77 240 if ! (nthreads > nprocs) { 78 241 fmt.Fprintf(os.Stderr, "Must have more threads than procs\n") … … 80 243 } 81 244 82 barrierStart := make(chan struct{})83 barrierSt op := make(chan struct{})84 threads_left = int64(nthreads )85 result := make(chan uint64)86 channels := make([] chan [] uint64, nthreads - nprocs)245 // Make global data 246 barrierStart := make(chan struct{}) // Barrier used at the start 247 threads_left = int64(nthreads - nspots) // Counter for active threads (not 'nthreads' because at all times 'nthreads - nprocs' are blocked) 248 result := make(chan Result) // Channel for results 249 channels := make([]Spot, nspots) // Number of spots 87 250 for i := range channels { 88 channels[i] = make(chan [] uint64, 1) 89 } 90 251 channels[i] = Spot{[16]uint64{0},uintptr(0), i,[16]uint64{0}} // init spots 252 } 253 254 // start the goroutines 91 255 for i := 0; i < nthreads; i++ { 92 go local(result, barrierStart, barrierStop, size, cnt, channels, uint64(nthreads - nprocs), share)256 go local(result, barrierStart, size, cnt, channels, share, i) 93 257 } 94 258 fmt.Printf("Starting\n"); 95 259 260 atomic.StoreInt32(&stop, 0) 96 261 start := time.Now() 97 close(barrierStart) 98 99 wait(start, true); 100 101 close(barrierStop)262 close(barrierStart) // release barrier 263 264 wait(start, true); // general benchmark wait 265 266 atomic.StoreInt32(&stop, 1) 102 267 end := time.Now() 103 268 delta := end.Sub(start) … … 105 270 fmt.Printf("\nDone\n") 106 271 107 global_counter := uint64(0) 272 // release all the blocked threads 273 for i := range channels { 274 channels[i].release() 275 } 276 277 // Join and accumulate results 278 results := NewResult() 108 279 for i := 0; i < nthreads; i++ { 109 global_counter += <- result 110 } 111 280 r := <- result 281 results.count += r.count 282 results.gmigs += r.gmigs 283 results.dmigs += r.dmigs 284 } 285 286 // Print with nice 's, i.e. 1'000'000 instead of 1000000 112 287 p := message.NewPrinter(language.English) 113 p.Printf("Duration ( ms): %f\n", delta.Seconds());288 p.Printf("Duration (s) : %f\n", delta.Seconds()); 114 289 p.Printf("Number of processors : %d\n", nprocs); 115 290 p.Printf("Number of threads : %d\n", nthreads); 116 291 p.Printf("Work size (64bit words): %d\n", size); 117 p.Printf("Total Operations(ops) : %15d\n", global_counter) 118 p.Printf("Ops per second : %18.2f\n", float64(global_counter) / delta.Seconds()) 119 p.Printf("ns per ops : %18.2f\n", float64(delta.Nanoseconds()) / float64(global_counter)) 120 p.Printf("Ops per threads : %15d\n", global_counter / uint64(nthreads)) 121 p.Printf("Ops per procs : %15d\n", global_counter / uint64(nprocs)) 122 p.Printf("Ops/sec/procs : %18.2f\n", (float64(global_counter) / float64(nprocs)) / delta.Seconds()) 123 p.Printf("ns per ops/procs : %18.2f\n", float64(delta.Nanoseconds()) / (float64(global_counter) / float64(nprocs))) 124 } 292 p.Printf("Total Operations(ops) : %15d\n", results.count) 293 p.Printf("Total G Migrations : %15d\n", results.gmigs) 294 p.Printf("Total D Migrations : %15d\n", results.dmigs) 295 p.Printf("Ops per second : %18.2f\n", float64(results.count) / delta.Seconds()) 296 p.Printf("ns per ops : %18.2f\n", float64(delta.Nanoseconds()) / float64(results.count)) 297 p.Printf("Ops per threads : %15d\n", results.count / uint64(nthreads)) 298 p.Printf("Ops per procs : %15d\n", results.count / uint64(nprocs)) 299 p.Printf("Ops/sec/procs : %18.2f\n", (float64(results.count) / float64(nprocs)) / delta.Seconds()) 300 p.Printf("ns per ops/procs : %18.2f\n", float64(delta.Nanoseconds()) / (float64(results.count) / float64(nprocs))) 301 } -
benchmark/readyQ/rq_bench.hfa
r42f6e07 r2b4daf2 39 39 } else if(stop_count > 0) { \ 40 40 clock_mode = false; \ 41 printf("Running for %l u iterations\n", stop_count); \41 printf("Running for %llu iterations\n", stop_count); \ 42 42 } else { \ 43 43 duration = 5; clock_mode = true;\ -
benchmark/readyQ/rq_bench.hpp
r42f6e07 r2b4daf2 97 97 } 98 98 99 bool parse_truefalse(const char * arg, bool & value) { 100 if(strcmp(arg, "true") == 0) { 101 value = true; 102 return true; 103 } 104 105 if(strcmp(arg, "false") == 0) { 106 value = false; 107 return true; 108 } 109 110 return false; 111 } 112 99 113 bool parse_settrue (const char *, bool & value ) { 100 114 value = true; … … 226 240 { 227 241 int idx = 0; 228 for( int i = 0; i < opt_count; i++) {242 for(size_t i = 0; i < opt_count; i++) { 229 243 if(options[i].long_name) { 230 244 optarr[idx].name = options[i].long_name; … … 256 270 { 257 271 int idx = 0; 258 for( int i = 0; i < opt_count; i++) {272 for(size_t i = 0; i < opt_count; i++) { 259 273 optstring[idx] = options[i].short_name; 260 274 idx++; … … 279 293 case 'h': 280 294 out = stdout; 295 [[fallthrough]]; 281 296 case '?': 282 297 usage(argv[0], options, opt_count, usage_msg, out); 283 298 default: 284 for( int i = 0; i < opt_count; i++) {299 for(size_t i = 0; i < opt_count; i++) { 285 300 if(opt == options[i].short_name) { 286 301 const char * arg = optarg ? optarg : ""; 302 if( arg[0] == '=' ) { arg++; } 287 303 bool success = options[i].parse_fun( arg, options[i].variable ); 288 304 if(success) goto NEXT_ARG; … … 319 335 int width = 0; 320 336 { 321 for( int i = 0; i < opt_count; i++) {337 for(size_t i = 0; i < opt_count; i++) { 322 338 if(options[i].long_name) { 323 339 int w = strlen(options[i].long_name); … … 338 354 fprintf(out, "Usage:\n %s %s\n", cmd, help); 339 355 340 for( int i = 0; i < opt_count; i++) {356 for(size_t i = 0; i < opt_count; i++) { 341 357 printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help); 342 358 } -
configure.ac
r42f6e07 r2b4daf2 295 295 # Some of our makefile don't need to be distributed 296 296 AM_CONDITIONAL([CFORALL_DISTRIBUTE], [test -e $TOP_SRCDIR/autogen.sh]) 297 AM_COND_IF([CFORALL_DISTRIBUTE], 298 [AC_CONFIG_FILES([297 AM_COND_IF([CFORALL_DISTRIBUTE], [ 298 AC_CONFIG_FILES([ 299 299 longrun_tests/Makefile 300 300 benchmark/Makefile … … 302 302 tools/Makefile 303 303 tools/prettyprinter/Makefile 304 ])]) 304 ]) 305 306 AC_OUTPUT(benchmark/Cargo.toml) 307 ]) 305 308 306 309 AC_CONFIG_LINKS([tests/test.py:tests/test.py]) -
doc/theses/thierry_delisle_PhD/thesis/Makefile
r42f6e07 r2b4daf2 28 28 base \ 29 29 empty \ 30 emptybit \ 31 emptytls \ 32 emptytree \ 33 fairness \ 30 34 system \ 31 35 } … … 73 77 ${LaTeX} $< 74 78 79 build/fairness.svg : fig/fairness.py | ${Build} 80 python3 $< $@ 81 75 82 ## Define the default recipes. 76 83 … … 88 95 fig2dev -L pstex_t -p ${Build}/$@ $< > ${Build}/$@_t 89 96 97 %.pstex : build/%.svg | ${Build} 98 inkscape -z -D --file=$< --export-eps=${Build}/$@ --export-latex 99 mv ${Build}/$@_tex ${Build}/$@_t 100 echo "sed -i 's/$@/${Build}/$@/g' ${Build}/$@_t" 101 sed -i 's/$@/${Build}\/$@/g' ${Build}/$@_t 102 90 103 ## pstex with inverted colors 91 104 %.dark.pstex : fig/%.fig Makefile | ${Build} -
doc/theses/thierry_delisle_PhD/thesis/glossary.tex
r42f6e07 r2b4daf2 7 7 \newacronym{io}{I/O}{Input and Output} 8 8 \newacronym{numa}{NUMA}{Non-Uniform Memory Access} 9 \newacronym{prng}{PRNG}{Pseudo Random Number Generator} 9 10 \newacronym{raii}{RAII}{Resource Acquisition Is Initialization} 10 11 \newacronym{tls}{TLS}{Thread Local Storage} -
doc/theses/thierry_delisle_PhD/thesis/local.bib
r42f6e07 r2b4daf2 609 609 note = "[Online; accessed 23-October-2020]" 610 610 } 611 612 @misc{wiki:lcg, 613 author = "{Wikipedia contributors}", 614 title = "Linear congruential generator --- {W}ikipedia{,} The Free Encyclopedia", 615 year = "2020", 616 url = "https://en.wikipedia.org/wiki/Linear_congruential_generator", 617 note = "[Online; accessed 2-January-2021]" 618 } -
doc/theses/thierry_delisle_PhD/thesis/text/core.tex
r42f6e07 r2b4daf2 1 1 \chapter{Scheduling Core}\label{core} 2 2 3 Before discussing scheduling in general, where it is important to address systems that are changing states, this document discusses scheduling in a somewhat ideal scenerio, where the system has reached a steady state. For this purpose, a steady state is loosely defined as a state where there are always \glspl{thrd} ready to run and but the system has the ressources necessary to accomplish the work. In short, the system is neither overloadedor underloaded.3 Before discussing scheduling in general, where it is important to address systems that are changing states, this document discusses scheduling in a somewhat ideal scenerio, where the system has reached a steady state. For this purpose, a steady state is loosely defined as a state where there are always \glspl{thrd} ready to run and the system has the ressources necessary to accomplish the work, \eg, enough workers. In short, the system is neither overloaded nor underloaded. 4 4 5 I believe it is important to discuss the steady state first because it is the easiest case to handle and, relatedly, the case in which the best performance is to be expected. As such, when the system is either overloaded or underloaded, a common approach is to try to adapt the system to the new load and return to the steady state . Flaws in the scheduling in the steady state tendtherefore to be pervasive in all states.5 I believe it is important to discuss the steady state first because it is the easiest case to handle and, relatedly, the case in which the best performance is to be expected. As such, when the system is either overloaded or underloaded, a common approach is to try to adapt the system to the new load and return to the steady state, \eg, adding or removing workers. Flaws in the scheduling when the system is in the steady state can therefore to be pervasive in all states. 6 6 7 7 \section{Design Goals} 8 As with most of the design decisions behind \CFA, the main goal is to match the expectation of the programmer, according to their probable mental model. To match these expectations, the design must offer the programmers sufficient guarantees so that, as long as the programmer respectsthe mental model, the system will also respect this model.8 As with most of the design decisions behind \CFA, an important goal is to match the expectation of the programmer, according to their probable mental model. To match these expectations, the design must offer the programmers sufficient guarantees so that, as long as they respect the mental model, the system will also respect this model. 9 9 10 10 For threading, a simple and common mental model is the ``Ideal multi-tasking CPU'' : … … 16 16 Applied to threads, this model states that every ready \gls{thrd} immediately runs in parallel with all other ready \glspl{thrd}. While a strict implementation of this model is not feasible, programmers still have expectations about scheduling that come from this model. 17 17 18 In general, the expectation at the center of this model is that ready \glspl{thrd} do not interfere with eachother but simply share the hardware. This makes it easier to reason about threading because ready \glspl{thrd} can be taken in isolation and the effect of the scheduler can be virtually ignored. This expectation of \gls{thrd} independence means the scheduler is expected to offer 2guarantees:18 In general, the expectation at the center of this model is that ready \glspl{thrd} do not interfere with eachother but simply share the hardware. This makes it easier to reason about threading because ready \glspl{thrd} can be taken in isolation and the effect of the scheduler can be virtually ignored. This expectation of \gls{thrd} independence means the scheduler is expected to offer two guarantees: 19 19 \begin{enumerate} 20 20 \item A fairness guarantee: a \gls{thrd} that is ready to run will not be prevented to do so by another thread. … … 24 24 It is important to note that these guarantees are expected only up to a point. \Glspl{thrd} that are ready to run should not be prevented to do so, but they still need to share a limited amount of hardware. Therefore, the guarantee is considered respected if a \gls{thrd} gets access to a \emph{fair share} of the hardware, even if that share is very small. 25 25 26 Similarly the performance guarantee, the lack of interferance between threads is only relevant op to a point. Ideally the cost of running and blocking would be constant regardless of contention, but the guarantee is considered satisfied if the cost is not \emph{too high} with or without contention. How much is an acceptable cost is obviously highly variable. For this document the performance experimentation will attempt to show that the cost of scheduling is not a major factor in application performance. This demonstration can be made by comparing application built in \CFA to applications built with other languages or other models. If the performance of an application built in \CFA is not meaningfully different than one built with a different runtime, then the scheduler has a negigeable impact on performance, \ie its impact can be ignored. Recall from a few paragraphs ago that the expectation of programmers is that the impact of the scheduler can be ignored. Therefore, if the cost of scheduling is not a significant portion of the runtime of several different application, I will consider the guarantee achieved.26 Similarly the performance guarantee, the lack of interferance between threads, is only relevant up to a point. Ideally the cost of running and blocking would be constant regardless of contention, but the guarantee is considered satisfied if the cost is not \emph{too high} with or without contention. How much is an acceptable cost is obviously highly variable. For this document the performance experimentation will attempt to show that the cost of scheduling is at worst equivalent to existing algorithms used in popular languages. This demonstration can be made by comparing application built in \CFA to applications built with other languages or other models. Recall from a few paragraphs ago that the expectation of programmers is that the impact of the scheduler can be ignored. Therefore, if the cost of scheduling is equivalent or lower to other popular languages, I will consider the guarantee achieved. 27 27 28 \todo{This paragraph should be moved later} 29 % The next step is then to decide what is considered a \emph{fair share}, \ie what metric is used to measure fairness. Since \CFA is intended to allow numerous short lived threads, I decided to avoid total CPU time as the measure of fairness. Total CPU time inherently favors new \glspl{thrd} over older ones which isn't necessarily a good thing. Instead, fairness is measured in terms of opportunities to run. This metric is more appropriate for a mix of short and long lived \glspl{thrd}. 28 More precisely the scheduler should be: 29 \begin{itemize} 30 \item As fast as other schedulers that are less fair. 31 \item Faster than other scheduler that have equal or better fairness. 32 \end{itemize} 33 34 \subsection{Fairness vs Scheduler Locality} 35 An important performance factor in modern architectures is cache locality. Waiting for data not present in the cache can have a major impact on performance, and having multiple \glspl{hthrd} writing to the same cache lines can lead to cache lines that need to be waited on again. It is therefore preferable to divide the data among each \gls{hthrd}\footnote{This can be an explicit division up front or using data structures where different \glspl{hthrd} are naturally routed to different cache lines.}. 36 37 For a scheduler, having good locality\footnote{This section discusses \emph{internal} locality, \ie, the locality of the data used by the scheduler. \emph{External locality}, \ie, how the data used by the application is affected by scheduling, is a much more complicated subject and will be discussed in the chapters on evaluation.}, \ie, having the data be local to each \gls{hthrd}, generally conflicts with fairness. Indeed, good locality often requires avoiding the movement of cache lines, while fairness requires dynamically moving \gls{thrd}, and as a consequence cache lines, to \glspl{hthrd} that are currently more appropriate. 38 39 However, I claim that in practice it is possible to strike a balance between fairness and performance because the need for these do not necessarily overlap temporaly. Figure~\ref{fig:fair} shows an visual representation of this behaviour. As mentionned, a little bit of unfairness can be acceptable, therefore it can be desirable to have an algorithm that prioritizes cache locality as long as no threads is left behind for too long. 40 41 \begin{figure} 42 \begin{center} 43 \input{fairness.pstex_t} 44 \end{center} 45 \caption{Fairness vs Locality} 46 \label{fig:fair} 47 Rule of thumb graph: Importance of Fairness and Locality while a ready \gls{thrd} waits run. 48 As the time a ready \gls{thrd} waits increases, ``Ready Time'', the chances that its data is still in cache decreases. At the same time, the need for fairness increases since other \glspl{thrd} may have the chance to run many times, breaking the fairness model mentionned above. Since the actual values and curves of this graph can be highly variable, the graph is left intentionally fuzzy and innacurate. 49 \end{figure} 30 50 31 51 \section{Design} 32 While avoiding the pitfalls of Feedback Scheduling is fairly easy, scheduling does not innately require feedback, avoiding prioritization of \glspl{thrd} is more difficult because of implicitly priorities, see Subsection~\ref{priority}. A strictly \glsxtrshort{fifo} rea 52 A naive strictly \glsxtrshort{fifo} ready-queue does not offer sufficient performance. As shown in the evaluation sections, most production schedulers scale when adding multiple \glspl{hthrd} and that is not possible with a single point of contention. Therefore it is vital to shard the ready-queue so that multiple \glspl{hthrd} can access the ready-queue without performance degradation. 33 53 34 \subsection{Sharding} 54 \subsection{Sharding} \label{sec:sharding} 55 An interesting approach to sharding a queue is presented in \cit{Trevors paper}. This algorithm represents a queue with relaxed \glsxtrshort{fifo} guarantee using an array of strictly \glsxtrshort{fifo} sublists as shown in Figure~\ref{fig:base}. Each cell of the array contains a linked-list with a lock and each node in these list is marked with a timestamp indicating when they were added to the list. Push operations are done by picking a random cell and attempting to push to its list. If the cell is already locked, the operation is simply retried on a new cell until a lock is acquired. Pop operations are done in a similar fashion except two random cells are picked. If both cells are not already locked and both cells contain non-empty lists, the operation pops the node with the oldest timestamp. If only one of the cell is unlocked and non-empty, the operation pops from that cell. If both cells are either locked or empty, the operation picks two new cells and tries again. 35 56 36 57 \begin{figure} … … 45 66 46 67 \subsection{Finding threads} 47 Once threads have been distributed onto multiple queues, indentifying which queues are empty and which aren't can become a problem. 48 Indeed, if the number of \glspl{thrd} does not far exceed the number of queues, it is probable that several of these queues are empty. 49 Figure~\ref{fig:empty} shows an example with 2 \glspl{thrd} running on 8 queues, where the chances of getting an empty queue is 75\% per pick, meaning two random picks yield a \gls{thrd} only half the time. 68 Once threads have been distributed onto multiple queues, indentifying which queues are empty and which are not can become a problem. Indeed, if the number of \glspl{thrd} does not far exceed the number of queues, it is probable that several of these queues are empty. Figure~\ref{fig:empty} shows an example with 2 \glspl{thrd} running on 8 queues, where the chances of getting an empty queue is 75\% per pick, meaning two random picks yield a \gls{thrd} only half the time. 50 69 51 70 … … 61 80 This can lead to performance problems since picks that do not yield a \gls{thrd} are not useful and do not necessarily help make more informed guesses. 62 81 63 Solutions to this problem can take many forms, but they ultimately all have to encode where the threads are in some form. My results show that the density and locality of this encoding is generally the dominating factor in these scheme. 82 Solutions to this problem can take many forms, but they ultimately all have to encode where the threads are in some form. My results show that the density and locality of this encoding is generally the dominating factor in these scheme. Classic solutions to this problem use one of three techniques to encode the information: 64 83 65 \paragraph{Dense Information} 84 \begin{figure} 85 \begin{center} 86 {\resizebox{0.73\textwidth}{!}{\input{emptybit.pstex_t}}} 87 \end{center} 88 \vspace*{-5pt} 89 \caption{Underloaded queue with added bitmask to indicate which array cells have items.} 90 \label{fig:emptybit} 91 \begin{center} 92 {\resizebox{0.73\textwidth}{!}{\input{emptytree.pstex_t}}} 93 \end{center} 94 \vspace*{-5pt} 95 \caption{Underloaded queue with added binary search tree indicate which array cells have items.} 96 \label{fig:emptytree} 97 \begin{center} 98 {\resizebox{0.9\textwidth}{!}{\input{emptytls.pstex_t}}} 99 \end{center} 100 \vspace*{-5pt} 101 \caption{Underloaded queue with added per processor bitmask to indicate which array cells have items.} 102 \label{fig:emptytls} 103 \end{figure} 104 105 \paragraph{Dense Information} Figure~\ref{fig:emptybit} shows a dense bitmask to identify which inner queues are currently in use. This approach means processors can often find \glspl{thrd} in constant time, regardless of how many underlying queues are empty. Furthermore, modern x86 CPUs have extended bit manipulation instructions (BMI2) that allow using the bitmask with very little overhead compared to the randomized selection approach for a filled ready queue, offering good performance even in cases with many empty inner queues. However, this technique has its limits: with a single word\footnote{Word refers here to however many bits can be written atomically.} bitmask, the total number of underlying queues in the ready queue is limited to the number of bits in the word. With a multi-word bitmask, this maximum limit can be increased arbitrarily, but the look-up will nolonger be constant time. Finally, a dense bitmap, either single or multi-word, causes additional contention problems which reduces performance because of cache misses after updates. This central update bottleneck also means the information in the bitmask is more often stale before a processor can use it to find an item, \ie mask read says there are available \glspl{thrd} but none on queue. 106 107 \paragraph{Sparse Information} Figure~\ref{fig:emptytree} shows an approach using a hierarchical tree data-structure to reduce contention and has been shown to work in similar cases~\cite{ellen2007snzi}. However, this approach may lead to poorer performance due to the inherent pointer chasing cost while still allowing more contention on the nodes of the tree if the tree is not deep enough. 108 109 \paragraph{Local Information} Figure~\ref{fig:emptytls} shows an approach using dense information, similar to the bitmap, but have each thread keep its own independent copy of it. While this approach can offer good scalability \emph{and} low latency, the liveliness and discovery of the information can become a problem. This case is made worst in systems with few processors where even blind random picks can find \glspl{thrd} in few tries. 110 111 I built a prototype of these approach and none of these techniques offer satisfying performance when few threads are present. All of these approach hit the same 2 problems. First, blindly picking two sub-queues is very fast which means that any improvement to the hit rate can easily be countered by a slow-down in look-up speed. Second, the array is already as sharded as is needed to avoid contention bottlenecks, so any denser data structure will tend to become a bottleneck. In all cases, these factors meant that the best cases scenerio, many threads, would get worst throughput and the worst case scenario, few threads, would get a better hit rate, but an equivalent throughput. As a result I tried an entirely different approach. 112 113 \subsection{Dynamic Entropy}\cit{https://xkcd.com/2318/} 114 In the worst case scenario there are few \glspl{thrd} ready to run, or more accuratly given $P$ \glspl{proc}, $T$ \glspl{thrd} and $\epsilon$, as usual, a very small number, in this case $\epsilon \ll P$, we have $T = P + \epsilon$. An important thing to note is that in this case, fairness is effectively irrelevant. Indeed, this case is close to \emph{actually matching} the model of the ``Ideal multi-tasking CPU'' presented in this chapter\footnote{For simplicity, this assumes there is a one-to-one match between \glspl{proc} and \glspl{hthrd}.}. Therefore, in this context it is possible to use a purely internal locality based approach and still meet the fairness requirements. This approach would simply have each \gls{proc} running a single \gls{thrd} repeatedly. Or from the shared ready-queue viewpoint, each \gls{proc} would push to a given sub-queue and then pop from the \emph{same} subqueue. Ideally, the scheduler would achieve this without affecting the fairness guarantees in cases where $T \gg P$. 115 116 To achieve this, I use a communication channel I have not mentionned yet and which I believe I use in a novel way : the \glsxtrshort{prng}. If the scheduler has a \glsxtrshort{prng} instance per \gls{proc} exclusively used for scheduling, its seed effectively encodes a list of all the accessed subqueues, from the latest to the oldest. The only requirement to achieve this is to be able to ``replay'' the \glsxtrshort{prng} backwards. As it turns out, this is an entirely reasonnable requirement and there already exist \glsxtrshort{prng}s that are fast, compact \emph{and} can be run forward and backwards. Linear congruential generators\cite{wiki:lcg} are an example of \glsxtrshort{prng}s that match these requirements. 117 118 The algorithm works as follows : 119 \begin{itemize} 120 \item Each \gls{proc} has two \glsxtrshort{prng} instances, $F$ and $B$. 121 \item Push and Pop operations happen as mentionned in Section~\ref{sec:sharding} with the following exceptions: 122 \begin{itemize} 123 \item Push operations use $F$ going forward on each try and on success $F$ is copied into $B$. 124 \item Pop operations use $B$ going backwards on each try. 125 \end{itemize} 126 \end{itemize} 127 128 The main benefit of this technique is that it basically repects the desired properties of Figure~\ref{fig:fair}. When looking for work, \glspl{proc} will look first at the last cells they pushed to, if any, and then move backwards through the cells. As the \glspl{proc} continue looking for work, $F$ moves back and $B$ stays in place. As a result the relation between the two becomes weaker, which means that the probablisitic fairness of the algorithm reverts to normal. Chapter~\ref{proofs} discusses more formally the fairness guarantees of this algorithm. 129 130 \section{Details} -
doc/theses/thierry_delisle_PhD/thesis/text/existing.tex
r42f6e07 r2b4daf2 42 42 \paragraph{Task Placement} Since modern computers rely heavily on cache hierarchies\cit{Do I need a citation for this}, migrating tasks from one core to another can be . \cite{DBLP:journals/tpds/SquillanteL93} 43 43 44 \ TODO{The survey is not great on this subject}44 \todo{The survey is not great on this subject} 45 45 46 46 \paragraph{Complex Machine Architecture} Another aspect that has been looked at is how well Work Stealing is applicable to different machine architectures. -
libcfa/src/Makefile.am
r42f6e07 r2b4daf2 58 58 concurrency/iofwd.hfa \ 59 59 containers/list.hfa \ 60 containers/stackLockFree.hfa 60 containers/stackLockFree.hfa \ 61 vec/vec.hfa \ 62 vec/vec2.hfa \ 63 vec/vec3.hfa \ 64 vec/vec4.hfa 61 65 62 66 inst_headers_src = \ … … 94 98 concurrency/clib/cfathread.h \ 95 99 concurrency/invoke.h \ 100 concurrency/future.hfa \ 96 101 concurrency/kernel/fwd.hfa 97 102 -
libcfa/src/bits/collection.hfa
r42f6e07 r2b4daf2 1 1 #pragma once 2 #include <stdio.h> // REMOVE THIS AFTER DEBUGGING 3 2 4 3 5 struct Colable { 4 Colable * next; // next node in the list6 struct Colable * next; // next node in the list 5 7 // invariant: (next != 0) <=> listed() 6 8 }; 7 8 inline {9 #ifdef __cforall 10 static inline { 9 11 // PUBLIC 10 12 … … 28 30 } 29 31 30 // wrappers to make Collection have T 31 forall( dtype T ) { 32 T *& Next( T * n ) { 33 return (T *)Next( (Colable *)n ); 34 } 35 36 bool listed( T * n ) { 37 return Next( (Colable *)n ) != 0p; 38 } 39 } // distribution 32 // // wrappers to make Collection have T 33 // forall( dtype T ) { 34 // T *& Next( T * n ) { 35 // return (T *)Next( (Colable *)n ); 36 // } 37 // } // distribution 40 38 } // distribution 41 39 40 forall( dtype T | { T *& Next ( T * ); } ) { 41 bool listed( T * n ) { 42 return Next( n ) != 0p; 43 } 44 } 42 45 43 46 struct Collection { … … 45 48 }; 46 49 47 inline {50 static inline { 48 51 // class invariant: root == 0 & empty() | *root in *this 49 52 void ?{}( Collection &, const Collection & ) = void; // no copy … … 65 68 66 69 struct ColIter { 67 void * curr; // element to be returned by >>70 void * curr; // element returned by | 68 71 }; 69 72 70 inline {73 static inline { 71 74 void ?{}( ColIter & colIter ) with( colIter ) { 72 75 curr = 0p; … … 79 82 } // distribution 80 83 } // distribution 84 #endif -
libcfa/src/bits/containers.hfa
r42f6e07 r2b4daf2 36 36 #define __small_array_t(T) __small_array(T) 37 37 #else 38 #define __small_array_t(T) struct__small_array38 #define __small_array_t(T) __small_array 39 39 #endif 40 40 -
libcfa/src/bits/defs.hfa
r42f6e07 r2b4daf2 29 29 #define __cfa_anonymous_object(x) inline struct x 30 30 #else 31 #define __cfa_anonymous_object(x) x __cfa_anonymous_object31 #define __cfa_anonymous_object(x) struct x __cfa_anonymous_object 32 32 #endif 33 33 -
libcfa/src/bits/locks.hfa
r42f6e07 r2b4daf2 283 283 void ^?{}(future_t &) {} 284 284 285 void reset(future_t & this) { 286 // needs to be in 0p or 1p 287 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 288 } 289 285 290 // check if the future is available 286 291 bool available( future_t & this ) { … … 340 345 341 346 // Mark the future as abandoned, meaning it will be deleted by the server 342 void abandon( future_t & this ) { 347 bool abandon( future_t & this ) { 348 /* paranoid */ verify( this.ptr != 3p ); 349 350 // Mark the future as abandonned 343 351 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST); 352 353 // If the future isn't already fulfilled, let the server delete it 354 if( got == 0p ) return false; 344 355 345 356 // got == 2p: the future is ready but the context hasn't fully been consumed … … 347 358 if( got == 2p ) { 348 359 while( this.ptr != 1p ) Pause(); 349 } 350 return; 360 got = 1p; 361 } 362 363 // The future is completed delete it now 364 /* paranoid */ verify( this.ptr != 1p ); 365 free( &this ); 366 return true; 351 367 } 352 368 -
libcfa/src/bits/queue.hfa
r42f6e07 r2b4daf2 3 3 #include "bits/collection.hfa" 4 4 5 forall( dtype T ) { 5 // A Queue(T) is a Collection(T) defining the ordering that nodes are returned by drop() in the same order from those 6 // added by add(). T must be a public descendant of uColable. 7 8 // The implementation is a typical singly-linked list, except the next field of the last element points to itself 9 // instead of being null. 10 11 forall( dtype T | { T *& Next ( T * ); } ) { 6 12 struct Queue { 7 13 inline Collection; // Plan 9 inheritance … … 34 40 } // post: n == tail() & succ(n) == 0 | n != tail() & *succ(n) in *q 35 41 36 voidaddHead( Queue(T) & q, T & n ) with( q ) {42 T & addHead( Queue(T) & q, T & n ) with( q ) { 37 43 #ifdef __CFA_DEBUG__ 38 44 if ( listed( &n ) ) abort( "(Queue &)%p.addHead( %p ) : Node is already on another list.", &q, &n ); … … 45 51 Next( &n ) = &n; // last node points to itself 46 52 } 53 return n; 47 54 } 48 55 49 voidaddTail( Queue(T) & q, T & n ) with( q ) {56 T & addTail( Queue(T) & q, T & n ) with( q ) { 50 57 #ifdef __CFA_DEBUG__ 51 58 if ( listed( &n ) ) abort( "(Queue &)%p.addTail( %p ) : Node is already on another list.", &q, &n ); … … 55 62 last = &n; 56 63 Next( &n ) = &n; // last node points to itself 64 return n; 57 65 } 58 66 59 voidadd( Queue(T) & q, T & n ) with( q ) {60 addTail( q, n );67 T & add( Queue(T) & q, T & n ) with( q ) { 68 return addTail( q, n ); 61 69 } 62 70 … … 64 72 T & t = head( q ); 65 73 if ( root ) { 66 root = Next( root );74 root = Next( (T *)root ); 67 75 if ( &head( q ) == &t ) { 68 76 root = last = 0p; // only one element … … 77 85 } 78 86 79 voidremove( Queue(T) & q, T & n ) with( q ) { // O(n)87 T & remove( Queue(T) & q, T & n ) with( q ) { // O(n) 80 88 #ifdef __CFA_DEBUG__ 81 89 if ( ! listed( (Colable &)n ) ) abort( "(Queue &)%p.remove( %p ) : Node is not on a list.", &q, &n ); … … 103 111 curr = Next( curr ); 104 112 } 113 return n; 105 114 } // post: ! listed( n ) 106 115 107 T & dropTail( Queue(T) & q ) with( q ) { 116 T & dropTail( Queue(T) & q ) with( q ) { // O(n) 108 117 T & n = tail( q ); 109 118 return &n ? remove( q, n ), n : *0p; … … 142 151 } // distribution 143 152 144 forall( dtype T ) {153 forall( dtype T | { T *& Next ( T * ); } ) { 145 154 struct QueueIter { 146 155 inline ColIter; // Plan 9 inheritance … … 152 161 } // post: curr == 0p 153 162 154 // create an iterator active in Queue q163 // create an iterator active in queue q 155 164 void ?{}( QueueIter(T) & qi, Queue(T) & q ) with( qi ) { 156 165 curr = &head( q ); … … 161 170 } // post: curr = {e in q} 162 171 163 // make existing iterator active in Queue q172 // make existing iterator active in queue q 164 173 void over( QueueIter(T) & qi, Queue(T) & q ) with( qi ) { 165 174 curr = &head( q ); 166 175 } // post: curr = {e in q} 167 176 168 bool ? >>?( QueueIter(T) & qi, T && tp ) with( qi ) {177 bool ?|?( QueueIter(T) & qi, T && tp ) with( qi ) { 169 178 if ( curr ) { 170 179 &tp = Curr( qi ); … … 174 183 return &tp != 0p; 175 184 } 176 // post: elts == null & !operator >>(tp) | elts != null & *tp' in elts & elts' == elts - *tp & operator>>(tp)185 // post: elts == null & !operator|(tp) | elts != null & *tp' in elts & elts' == elts - *tp & operator|(tp) 177 186 } // distribution 178 187 } // distribution -
libcfa/src/bits/sequence.hfa
r42f6e07 r2b4daf2 2 2 3 3 #include "bits/collection.hfa" 4 #include "bits/defs.hfa" 4 5 5 6 struct Seqable { 6 inline Colable;7 Seqable * back; // pointer to previous node in the list7 __cfa_anonymous_object(Colable); 8 struct Seqable * back; // pointer to previous node in the list 8 9 }; 9 10 10 inline { 11 #ifdef __cforall 12 static inline { 11 13 // PUBLIC 12 14 … … 26 28 } 27 29 28 // wrappers to make Collection have T29 forall( dtype T ) {30 T *& Back( T * n ) {31 return (T *)Back( (Seqable *)n );32 }33 } // distribution30 // // wrappers to make Collection have T 31 // forall( dtype T ) { 32 // T *& Back( T * n ) { 33 // return (T *)Back( (Seqable *)n ); 34 // } 35 // } // distribution 34 36 } // distribution 35 37 36 forall( dtype T ) { 38 39 // A Sequence(T) is a Collection(T) defining the ordering of a uStack and uQueue, and to insert and remove elements 40 // anywhere in the sequence. T must be a public descendant of uSeqable. 41 42 // The implementation is a typical doubly-linked list, except the next field of the last node points at the first node 43 // and the back field of the last node points at the first node (circular). 44 45 forall( dtype T | { T *& Back ( T * ); T *& Next ( T * ); } ) { 37 46 struct Sequence { 38 47 inline Collection; // Plan 9 inheritance 39 48 }; 40 49 41 inline {50 static inline { 42 51 // wrappers to make Collection have T 43 52 T & head( Sequence(T) & s ) with( s ) { … … 50 59 void ?{}( Sequence(T) & s ) with( s ) { 51 60 ((Collection &)s){}; 52 } // post: isEmpty() .53 54 // Return a pointer to the last sequence element, without removing it. 61 } // post: isEmpty() 62 63 // Return a pointer to the last sequence element, without removing it. 55 64 T & tail( Sequence(T) & s ) with( s ) { 56 65 return root ? (T &)*Back( &head( s ) ) : *0p; … … 65 74 } // post: n == tail() & succ(n) == 0 | n != tail() & *succ(n) in *s 66 75 67 // Return a pointer to the element before *n, or 0p if there isn't one.76 // Return a pointer to the element before *n, or 0p if list empty. 68 77 T * pred( Sequence(T) & s, T * n ) with( s ) { // pre: *n in *s 69 78 #ifdef __CFA_DEBUG__ … … 71 80 #endif // __CFA_DEBUG__ 72 81 return n == &head( s ) ? 0p : Back( n ); 73 } 74 75 76 // Insert *n into the sequence before *bef, or at the end if bef == 0 .77 voidinsertBef( Sequence(T) & s, T & n, T & bef ) with( s ) { // pre: !n->listed() & *bef in *s82 } // post: n == head() & head(n) == 0 | n != head() & *pred(n) in *s 83 84 85 // Insert *n into the sequence before *bef, or at the end if bef == 0p. 86 T & insertBef( Sequence(T) & s, T & n, T & bef ) with( s ) { // pre: !n->listed() & *bef in *s 78 87 #ifdef __CFA_DEBUG__ 79 88 if ( listed( &n ) ) abort( "(Sequence &)%p.insertBef( %p, %p ) : Node is already on another list.", &s, n, &bef ); … … 103 112 Next( Back( &n ) ) = &n; 104 113 } // if 114 return n; 105 115 } // post: n->listed() & *n in *s & succ(n) == bef 106 116 107 117 108 118 // Insert *n into the sequence after *aft, or at the beginning if aft == 0. 109 voidinsertAft( Sequence(T) & s, T & aft, T & n ) with( s ) { // pre: !n->listed() & *aft in *s119 T & insertAft( Sequence(T) & s, T & aft, T & n ) with( s ) { // pre: !n->listed() & *aft in *s 110 120 #ifdef __CFA_DEBUG__ 111 121 if ( listed( &n ) ) abort( "(Sequence &)%p.insertAft( %p, %p ) : Node is already on another list.", &s, &aft, &n ); … … 133 143 Next( &aft ) = &n; 134 144 } // if 135 } // post: n->listed() & *n in *s & succ(n) == bef 145 return n; 146 } // post: n->listed() & *n in *s & succ(n) == bef 136 147 137 148 // pre: n->listed() & *n in *s 138 voidremove( Sequence(T) & s, T & n ) with( s ) { // O(1)149 T & remove( Sequence(T) & s, T & n ) with( s ) { // O(1) 139 150 #ifdef __CFA_DEBUG__ 140 151 if ( ! listed( &n ) ) abort( "(Sequence &)%p.remove( %p ) : Node is not on a list.", &s, &n ); … … 147 158 Next( Back( &n ) ) = Next( &n ); 148 159 Next( &n ) = Back( &n ) = 0p; 149 } // post: !n->listed(). 160 return n; 161 } // post: !n->listed() 150 162 151 163 // Add an element to the head of the sequence. 152 void addHead( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 153 insertAft( s, *0p, n ); 154 } 164 T & addHead( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 165 return insertAft( s, *0p, n ); 166 } 167 155 168 // Add an element to the tail of the sequence. 156 void addTail( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 157 insertBef( s, n, *0p ); 158 } 169 T & addTail( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 170 return insertBef( s, n, *0p ); 171 } 172 159 173 // Add an element to the tail of the sequence. 160 void add( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 161 addTail( s, n ); 162 } 174 T & add( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 175 return addTail( s, n ); 176 } 177 163 178 // Remove and return the head element in the sequence. 164 179 T & dropHead( Sequence(T) & s ) { … … 166 181 return &n ? remove( s, n ), n : *0p; 167 182 } 183 168 184 // Remove and return the head element in the sequence. 169 185 T & drop( Sequence(T) & s ) { 170 186 return dropHead( s ); 171 187 } 188 172 189 // Remove and return the tail element in the sequence. 173 190 T & dropTail( Sequence(T) & s ) { … … 184 201 T * toEnd = Back( &head( s ) ); 185 202 T * fromEnd = Back( &head( from ) ); 186 Back( root ) = fromEnd;203 Back( (T *)root ) = fromEnd; 187 204 Next( fromEnd ) = &head( s ); 188 Back( from.root ) = toEnd;205 Back( (T *)from.root ) = toEnd; 189 206 Next( toEnd ) = &head( from ); 190 207 } // if … … 214 231 } // distribution 215 232 216 forall( dtype T ) {233 forall( dtype T | { T *& Back ( T * ); T *& Next ( T * ); } ) { 217 234 // SeqIter(T) is used to iterate over a Sequence(T) in head-to-tail order. 218 235 struct SeqIter { … … 224 241 }; 225 242 226 inline {243 static inline { 227 244 void ?{}( SeqIter(T) & si ) with( si ) { 228 245 ((ColIter &)si){}; 229 246 seq = 0p; 230 } // post: elts = null. 231 247 } // post: elts = null 248 249 // Create a iterator active in sequence s. 232 250 void ?{}( SeqIter(T) & si, Sequence(T) & s ) with( si ) { 233 251 ((ColIter &)si){}; 234 252 seq = &s; 235 253 curr = &head( s ); 236 } // post: elts = null .254 } // post: elts = null 237 255 238 256 void ?{}( SeqIter(T) & si, Sequence(T) & s, T & start ) with( si ) { … … 240 258 seq = &s; 241 259 curr = &start; 242 } // post: elts = null. 243 260 } // post: elts = null 261 262 // Make the iterator active in sequence s. 244 263 void over( SeqIter(T) & si, Sequence(T) & s ) with( si ) { 245 264 seq = &s; 246 265 curr = &head( s ); 247 } // post: elts = {e in s} .248 249 bool ? >>?( SeqIter(T) & si, T && tp ) with( si ) {266 } // post: elts = {e in s} 267 268 bool ?|?( SeqIter(T) & si, T && tp ) with( si ) { 250 269 if ( curr ) { 251 270 &tp = Curr( si ); … … 265 284 }; 266 285 267 inline {286 static inline { 268 287 void ?{}( SeqIterRev(T) & si ) with( si ) { 269 288 ((ColIter &)si){}; 270 289 seq = 0p; 271 } // post: elts = null. 272 290 } // post: elts = null 291 292 // Create a iterator active in sequence s. 273 293 void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 274 294 ((ColIter &)si){}; 275 295 seq = &s; 276 296 curr = &tail( s ); 277 } // post: elts = null .297 } // post: elts = null 278 298 279 299 void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) { … … 281 301 seq = &s; 282 302 curr = &start; 283 } // post: elts = null. 284 303 } // post: elts = null 304 305 // Make the iterator active in sequence s. 285 306 void over( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 286 307 seq = &s; 287 308 curr = &tail( s ); 288 } // post: elts = {e in s} .289 290 bool ? >>?( SeqIterRev(T) & si, T && tp ) with( si ) {309 } // post: elts = {e in s} 310 311 bool ?|?( SeqIterRev(T) & si, T && tp ) with( si ) { 291 312 if ( curr ) { 292 313 &tp = Curr( si ); … … 298 319 } // distribution 299 320 } // distribution 321 322 #endif -
libcfa/src/bits/stack.hfa
r42f6e07 r2b4daf2 3 3 #include "bits/collection.hfa" 4 4 5 forall( dtype T ) { 5 // A Stack(T) is a Collection(T) defining the ordering that nodes are returned by drop() in the reverse order from those 6 // added by add(). T must be a public descendant of uColable. 7 8 // The implementation is a typical singly-linked list, except the next field of the last element points to itself 9 // instead of being null. 10 11 forall( dtype T | { T *& Next ( T * ); } ) { 6 12 struct Stack { 7 13 inline Collection; // Plan 9 inheritance … … 25 31 } 26 32 27 voidaddHead( Stack(T) & s, T & n ) with( s ) {33 T & addHead( Stack(T) & s, T & n ) with( s ) { 28 34 #ifdef __CFA_DEBUG__ 29 35 if ( listed( (Colable &)(n) ) ) abort( "(Stack &)%p.addHead( %p ) : Node is already on another list.", &s, n ); … … 31 37 Next( &n ) = &head( s ) ? &head( s ) : &n; 32 38 root = &n; 39 return n; 33 40 } 34 41 35 voidadd( Stack(T) & s, T & n ) with( s ) {36 addHead( s, n );42 T & add( Stack(T) & s, T & n ) with( s ) { 43 return addHead( s, n ); 37 44 } 38 45 39 voidpush( Stack(T) & s, T & n ) with( s ) {40 addHead( s, n );46 T & push( Stack(T) & s, T & n ) with( s ) { 47 return addHead( s, n ); 41 48 } 42 49 … … 44 51 T & t = head( s ); 45 52 if ( root ) { 46 root = ( T *)Next( root );53 root = ( T *)Next( (T *)root ); 47 54 if ( &head( s ) == &t ) root = 0p; // only one element ? 48 55 Next( &t ) = 0p; … … 57 64 } // distribution 58 65 66 // A StackIter(T) is a subclass of ColIter(T) that generates the elements of a Stack(T). It returns the elements in the 67 // order returned by drop(). 59 68 60 forall( dtype T ) {69 forall( dtype T | { T *& Next ( T * ); } ) { 61 70 struct StackIter { 62 71 inline ColIter; // Plan 9 inheritance … … 68 77 } // post: curr == 0p 69 78 70 // create an iterator active in Stack s79 // create an iterator active in stack s 71 80 void ?{}( StackIter(T) & si, Stack(T) & s ) with( si ) { 72 81 curr = &head( s ); … … 77 86 } // post: curr = {e in s} 78 87 79 // make existing iterator active in Stack q88 // make existing iterator active in stack s 80 89 void over( StackIter(T) & si, Stack(T) & s ) with( si ) { 81 90 curr = &head( s ); 82 91 } // post: curr = {e in s} 83 92 84 bool ? >>?( StackIter(T) & si, T && tp ) with( si ) {93 bool ?|?( StackIter(T) & si, T && tp ) with( si ) { 85 94 if ( curr ) { 86 95 &tp = Curr( si ); -
libcfa/src/concurrency/invoke.h
r42f6e07 r2b4daf2 189 189 struct __monitor_group_t monitors; 190 190 191 // used to put threads on user data structures 192 struct { 193 struct $thread * next; 194 struct $thread * back; 195 } seqable; 196 191 197 struct { 192 198 struct $thread * next; … … 218 224 } 219 225 226 static inline $thread *& Back( $thread * this ) __attribute__((const)) { 227 return this->seqable.back; 228 } 229 230 static inline $thread *& Next( $thread * this ) __attribute__((const)) { 231 return this->seqable.next; 232 } 233 234 static inline bool listed( $thread * this ) { 235 return this->seqable.next != 0p; 236 } 237 220 238 static inline void ?{}(__monitor_group_t & this) { 221 239 (this.data){0p}; -
libcfa/src/concurrency/kernel/startup.cfa
r42f6e07 r2b4daf2 118 118 119 119 extern size_t __page_size; 120 extern int __map_prot; 120 121 121 122 //----------------------------------------------------------------------------- … … 725 726 } 726 727 #else 728 __cfaabi_dbg_debug_do( 729 // pthread has no mechanism to create the guard page in user supplied stack. 730 if ( mprotect( stack, __page_size, __map_prot ) == -1 ) { 731 abort( "mprotect : internal error, mprotect failure, error(%d) %s.", errno, strerror( errno ) ); 732 } // if 733 ); 727 734 free( stack ); 728 735 #endif -
libcfa/src/concurrency/locks.cfa
r42f6e07 r2b4daf2 1 1 #include "locks.hfa" 2 2 #include "kernel_private.hfa" 3 #include <stdlib.h>4 #include <stdio.h>5 3 6 4 #include <kernel.hfa> 7 5 #include <stdlib.hfa> 8 #include <thread.hfa> 9 10 /////////////////////////////////////////////////////////////////// 11 //// info_thread 12 /////////////////////////////////////////////////////////////////// 13 6 7 //----------------------------------------------------------------------------- 8 // info_thread 14 9 forall(dtype L | is_blocking_lock(L)) { 15 void ?{}( info_thread(L) & this, $thread * t ) { 16 ((Seqable &) this){}; 17 this.t = t; 18 this.lock = 0p; 19 this.listed = false; 20 } 21 22 void ?{}( info_thread(L) & this, $thread * t, uintptr_t info ) { 10 struct info_thread { 11 // used to put info_thread on a dl queue (aka sequence) 12 inline Seqable; 13 14 // waiting thread 15 struct $thread * t; 16 17 // shadow field 18 uintptr_t info; 19 20 // lock that is passed to wait() (if one is passed) 21 L * lock; 22 23 // true when signalled and false when timeout wakes thread 24 bool signalled; 25 }; 26 27 void ?{}( info_thread(L) & this, $thread * t, uintptr_t info, L * l ) { 23 28 ((Seqable &) this){}; 24 29 this.t = t; 25 30 this.info = info; 26 this.lock = 0p; 27 this.listed = false; 28 } 29 30 void ^?{}( info_thread(L) & this ){ } 31 } 32 33 /////////////////////////////////////////////////////////////////// 34 //// Blocking Locks 35 /////////////////////////////////////////////////////////////////// 36 31 this.lock = l; 32 } 33 34 void ^?{}( info_thread(L) & this ) {} 35 36 info_thread(L) *& Back( info_thread(L) * this ) { 37 return (info_thread(L) *)Back( (Seqable *)this ); 38 } 39 40 info_thread(L) *& Next( info_thread(L) * this ) { 41 return (info_thread(L) *)Next( (Colable *)this ); 42 } 43 } 44 45 //----------------------------------------------------------------------------- 46 // Blocking Locks 37 47 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ) { 38 48 this.lock{}; … … 46 56 47 57 void ^?{}( blocking_lock & this ) {} 48 void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}58 void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };} 49 59 void ^?{}( single_acquisition_lock & this ) {} 50 void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}60 void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };} 51 61 void ^?{}( owner_lock & this ) {} 52 void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };}62 void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };} 53 63 void ^?{}( multiple_acquisition_lock & this ) {} 54 64 55 65 void lock( blocking_lock & this ) with( this ) { 56 66 lock( lock __cfaabi_dbg_ctx2 ); 57 if ( owner == active_thread() && !multi_acquisition) { 58 abort("A single acquisition lock holder attempted to reacquire the lock resulting in a deadlock."); 59 } else if ( owner != 0p && owner != active_thread() ) { 60 append( blocked_threads, active_thread() ); 67 $thread * thrd = active_thread(); 68 69 // single acquisition lock is held by current thread 70 /* paranoid */ verifyf( owner != thrd || multi_acquisition, "Single acquisition lock holder (%p) attempted to reacquire the lock %p resulting in a deadlock.", owner, &this ); 71 72 // lock is held by some other thread 73 if ( owner != 0p && owner != thrd ) { 74 addTail( blocked_threads, *thrd ); 61 75 wait_count++; 62 76 unlock( lock ); 63 77 park( ); 64 } else if ( owner == active_thread() && multi_acquisition ) { 78 } 79 // multi acquisition lock is held by current thread 80 else if ( owner == thrd && multi_acquisition ) { 65 81 recursion_count++; 66 82 unlock( lock ); 67 } else { 68 owner = active_thread(); 83 } 84 // lock isn't held 85 else { 86 owner = thrd; 69 87 recursion_count = 1; 70 88 unlock( lock ); … … 75 93 bool ret = false; 76 94 lock( lock __cfaabi_dbg_ctx2 ); 95 96 // lock isn't held 77 97 if ( owner == 0p ) { 78 98 owner = active_thread(); 79 99 recursion_count = 1; 80 100 ret = true; 81 } else if ( owner == active_thread() && multi_acquisition ) { 101 } 102 // multi acquisition lock is held by current thread 103 else if ( owner == active_thread() && multi_acquisition ) { 82 104 recursion_count++; 83 105 ret = true; 84 106 } 107 85 108 unlock( lock ); 86 109 return ret; 87 110 } 88 111 89 void unlock_error_check( blocking_lock & this ) with( this ) {90 if ( owner == 0p ){ // no owner implies lock isn't held91 abort( "There was an attempt to release a lock that isn't held" );92 } else if ( strict_owner && owner != active_thread() ) {93 abort( "A thread other than the owner attempted to release an owner lock" );94 }95 }96 97 112 void pop_and_set_new_owner( blocking_lock & this ) with( this ) { 98 $thread * t = pop_head( blocked_threads );113 $thread * t = &dropHead( blocked_threads ); 99 114 owner = t; 100 115 recursion_count = ( t ? 1 : 0 ); … … 105 120 void unlock( blocking_lock & this ) with( this ) { 106 121 lock( lock __cfaabi_dbg_ctx2 ); 107 unlock_error_check( this ); 122 /* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this ); 123 /* paranoid */ verifyf( owner == active_thread() || !strict_owner, "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this ); 124 125 // if recursion count is zero release lock and set new owner if one is waiting 108 126 recursion_count--; 109 127 if ( recursion_count == 0 ) { … … 125 143 } 126 144 127 void add_( blocking_lock & this, $thread * t ) with( this ) { 128 lock( lock __cfaabi_dbg_ctx2 ); 145 void on_notify( blocking_lock & this, $thread * t ) with( this ) { 146 lock( lock __cfaabi_dbg_ctx2 ); 147 // lock held 129 148 if ( owner != 0p ) { 130 a ppend( blocked_threads,t );149 addTail( blocked_threads, *t ); 131 150 wait_count++; 132 151 unlock( lock ); 133 } else { 152 } 153 // lock not held 154 else { 134 155 owner = t; 135 156 recursion_count = 1; … … 139 160 } 140 161 141 void remove_( blocking_lock & this ) with( this ) { 142 lock( lock __cfaabi_dbg_ctx2 ); 143 unlock_error_check( this ); 162 void on_wait( blocking_lock & this ) with( this ) { 163 lock( lock __cfaabi_dbg_ctx2 ); 164 /* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this ); 165 /* paranoid */ verifyf( owner == active_thread() || !strict_owner, "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this ); 166 144 167 pop_and_set_new_owner( this ); 145 168 unlock( lock ); 146 169 } 147 170 148 /////////////////////////////////////////////////////////////////// 149 //// Overloaded routines for traits 150 /////////////////////////////////////////////////////////////////// 151 152 // This is temporary until an inheritance bug is fixed 153 154 void lock( single_acquisition_lock & this ){ lock( (blocking_lock &)this ); } 155 void unlock( single_acquisition_lock & this ){ unlock( (blocking_lock &)this ); } 156 void add_( single_acquisition_lock & this, struct $thread * t ){ add_( (blocking_lock &)this, t ); } 157 void remove_( single_acquisition_lock & this ){ remove_( (blocking_lock &)this ); } 158 void set_recursion_count( single_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); } 159 size_t get_recursion_count( single_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); } 160 161 void lock( owner_lock & this ){ lock( (blocking_lock &)this ); } 162 void unlock( owner_lock & this ){ unlock( (blocking_lock &)this ); } 163 void add_( owner_lock & this, struct $thread * t ){ add_( (blocking_lock &)this, t ); } 164 void remove_( owner_lock & this ){ remove_( (blocking_lock &)this ); } 165 void set_recursion_count( owner_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); } 166 size_t get_recursion_count( owner_lock & this ){ return get_recursion_count( (blocking_lock &)this ); } 167 168 void lock( multiple_acquisition_lock & this ){ lock( (blocking_lock &)this ); } 169 void unlock( multiple_acquisition_lock & this ){ unlock( (blocking_lock &)this ); } 170 void add_( multiple_acquisition_lock & this, struct $thread * t ){ add_( (blocking_lock &)this, t ); } 171 void remove_( multiple_acquisition_lock & this ){ remove_( (blocking_lock &)this ); } 172 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); } 171 //----------------------------------------------------------------------------- 172 // Overloaded routines for traits 173 // These routines are temporary until an inheritance bug is fixed 174 void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 175 void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 176 void on_wait ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); } 177 void on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 178 void set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 179 size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 180 181 void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); } 182 void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); } 183 void on_wait ( owner_lock & this ) { on_wait( (blocking_lock &)this ); } 184 void on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 185 void set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 186 size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 187 188 void lock ( multiple_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 189 void unlock ( multiple_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 190 void on_wait ( multiple_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); } 191 void on_notify( multiple_acquisition_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); } 192 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); } 173 193 size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); } 174 194 175 /////////////////////////////////////////////////////////////////// 176 //// condition variable 177 /////////////////////////////////////////////////////////////////// 178 195 //----------------------------------------------------------------------------- 196 // alarm node wrapper 179 197 forall(dtype L | is_blocking_lock(L)) { 198 struct alarm_node_wrap { 199 alarm_node_t alarm_node; 200 condition_variable(L) * cond; 201 info_thread(L) * i; 202 }; 203 204 void ?{}( alarm_node_wrap(L) & this, Time alarm, Duration period, Alarm_Callback callback, condition_variable(L) * c, info_thread(L) * i ) { 205 this.alarm_node{ callback, alarm, period }; 206 this.cond = c; 207 this.i = i; 208 } 209 210 void ^?{}( alarm_node_wrap(L) & this ) { } 180 211 181 212 void timeout_handler ( alarm_node_wrap(L) & this ) with( this ) { 182 // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin. 183 lock( cond->lock __cfaabi_dbg_ctx2 ); 184 185 if ( i->listed ) { // is thread on queue 186 cond->last_thread = i; // REMOVE THIS AFTER DEBUG 187 remove( cond->blocked_threads, *i ); //remove this thread O(1) 213 // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin. 214 lock( cond->lock __cfaabi_dbg_ctx2 ); 215 216 // this check is necessary to avoid a race condition since this timeout handler 217 // may still be called after a thread has been removed from the queue but 218 // before the alarm is unregistered 219 if ( listed(i) ) { // is thread on queue 220 i->signalled = false; 221 // remove this thread O(1) 222 remove( cond->blocked_threads, *i ); 188 223 cond->count--; 189 if( !i->lock ) { 224 if( i->lock ) { 225 // call lock's on_notify if a lock was passed 226 on_notify(*i->lock, i->t); 227 } else { 228 // otherwise wake thread 190 229 unpark( i->t ); 191 } else { 192 add_(*i->lock, i->t); // call lock's add_ 193 } 194 } 195 unlock( cond->lock ); 196 } 197 230 } 231 } 232 unlock( cond->lock ); 233 } 234 235 // this casts the alarm node to our wrapped type since we used type erasure 198 236 void alarm_node_wrap_cast( alarm_node_t & a ) { timeout_handler( (alarm_node_wrap(L) &)a ); } 237 } 238 239 //----------------------------------------------------------------------------- 240 // condition variable 241 forall(dtype L | is_blocking_lock(L)) { 199 242 200 243 void ?{}( condition_variable(L) & this ){ … … 202 245 this.blocked_threads{}; 203 246 this.count = 0; 204 this.last_thread = 0p; // REMOVE AFTER DEBUG205 247 } 206 248 207 249 void ^?{}( condition_variable(L) & this ){ } 208 209 void ?{}( alarm_node_wrap(L) & this, Time alarm, Duration period, Alarm_Callback callback ) {210 this.alarm_node{ callback, alarm, period };211 }212 213 void ^?{}( alarm_node_wrap(L) & this ) { }214 250 215 251 void process_popped( condition_variable(L) & this, info_thread(L) & popped ) with( this ) { 216 252 if(&popped != 0p) { 217 popped. listed = false;253 popped.signalled = true; 218 254 count--; 219 255 if (popped.lock) { 220 add_(*popped.lock, popped.t); 256 // if lock passed call on_notify 257 on_notify(*popped.lock, popped.t); 221 258 } else { 259 // otherwise wake thread 222 260 unpark(popped.t); 223 261 } … … 252 290 253 291 size_t queue_and_get_recursion( condition_variable(L) & this, info_thread(L) * i ) with(this) { 292 // add info_thread to waiting queue 254 293 addTail( blocked_threads, *i ); 255 294 count++; 256 i->listed = true;257 295 size_t recursion_count = 0; 258 296 if (i->lock) { 259 i->t->link.next = 1p;297 // if lock was passed get recursion count to reset to after waking thread 260 298 recursion_count = get_recursion_count(*i->lock); 261 remove_( *i->lock );299 on_wait( *i->lock ); 262 300 } 263 301 return recursion_count; … … 269 307 size_t recursion_count = queue_and_get_recursion(this, &i); 270 308 unlock( lock ); 271 park( ); // blocks here 272 if (i.lock) set_recursion_count(*i.lock, recursion_count); // resets recursion count here after waking 273 } 309 310 // blocks here 311 park( ); 312 313 // resets recursion count here after waking 314 if (i.lock) set_recursion_count(*i.lock, recursion_count); 315 } 316 317 #define WAIT( u, l ) \ 318 info_thread( L ) i = { active_thread(), u, l }; \ 319 queue_info_thread( this, i ); 274 320 275 321 // helper for wait()'s' with a timeout … … 277 323 lock( lock __cfaabi_dbg_ctx2 ); 278 324 size_t recursion_count = queue_and_get_recursion(this, &info); 279 alarm_node_wrap(L) node_wrap = { t, 0`s, alarm_node_wrap_cast }; 280 node_wrap.cond = &this; 281 node_wrap.i = &info; 325 alarm_node_wrap(L) node_wrap = { t, 0`s, alarm_node_wrap_cast, &this, &info }; 282 326 register_self( &node_wrap.alarm_node ); 283 327 unlock( lock ); 328 329 // blocks here 284 330 park(); 331 332 // unregisters alarm so it doesn't go off if this happens first 285 333 unregister_self( &node_wrap.alarm_node ); 334 335 // resets recursion count here after waking 286 336 if (info.lock) set_recursion_count(*info.lock, recursion_count); 287 337 } 288 338 289 void wait( condition_variable(L) & this ) with(this) { 290 info_thread( L ) i = { active_thread() }; 291 queue_info_thread( this, i ); 292 } 293 294 void wait( condition_variable(L) & this, uintptr_t info ) with(this) { 295 info_thread( L ) i = { active_thread(), info }; 296 queue_info_thread( this, i ); 297 } 298 299 void wait( condition_variable(L) & this, Duration duration ) with(this) { 300 info_thread( L ) i = { active_thread() }; 301 queue_info_thread_timeout(this, i, __kernel_get_time() + duration ); 302 } 303 304 void wait( condition_variable(L) & this, uintptr_t info, Duration duration ) with(this) { 305 info_thread( L ) i = { active_thread(), info }; 306 queue_info_thread_timeout(this, i, __kernel_get_time() + duration ); 307 } 308 309 void wait( condition_variable(L) & this, Time time ) with(this) { 310 info_thread( L ) i = { active_thread() }; 311 queue_info_thread_timeout(this, i, time); 312 } 313 314 void wait( condition_variable(L) & this, uintptr_t info, Time time ) with(this) { 315 info_thread( L ) i = { active_thread(), info }; 316 queue_info_thread_timeout(this, i, time); 317 } 318 319 void wait( condition_variable(L) & this, L & l ) with(this) { 320 info_thread(L) i = { active_thread() }; 321 i.lock = &l; 322 queue_info_thread( this, i ); 323 } 324 325 void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) { 326 info_thread(L) i = { active_thread(), info }; 327 i.lock = &l; 328 queue_info_thread( this, i ); 329 } 330 331 void wait( condition_variable(L) & this, L & l, Duration duration ) with(this) { 332 info_thread(L) i = { active_thread() }; 333 i.lock = &l; 334 queue_info_thread_timeout(this, i, __kernel_get_time() + duration ); 335 } 336 337 void wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) { 338 info_thread(L) i = { active_thread(), info }; 339 i.lock = &l; 340 queue_info_thread_timeout(this, i, __kernel_get_time() + duration ); 341 } 342 343 void wait( condition_variable(L) & this, L & l, Time time ) with(this) { 344 info_thread(L) i = { active_thread() }; 345 i.lock = &l; 346 queue_info_thread_timeout(this, i, time ); 347 } 348 349 void wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) { 350 info_thread(L) i = { active_thread(), info }; 351 i.lock = &l; 352 queue_info_thread_timeout(this, i, time ); 353 } 354 } 339 #define WAIT_TIME( u, l, t ) \ 340 info_thread( L ) i = { active_thread(), u, l }; \ 341 queue_info_thread_timeout(this, i, t ); \ 342 return i.signalled; 343 344 void wait( condition_variable(L) & this ) with(this) { WAIT( 0, 0p ) } 345 void wait( condition_variable(L) & this, uintptr_t info ) with(this) { WAIT( info, 0p ) } 346 void wait( condition_variable(L) & this, L & l ) with(this) { WAIT( 0, &l ) } 347 void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) { WAIT( info, &l ) } 348 349 bool wait( condition_variable(L) & this, Duration duration ) with(this) { WAIT_TIME( 0 , 0p , __kernel_get_time() + duration ) } 350 bool wait( condition_variable(L) & this, uintptr_t info, Duration duration ) with(this) { WAIT_TIME( info, 0p , __kernel_get_time() + duration ) } 351 bool wait( condition_variable(L) & this, Time time ) with(this) { WAIT_TIME( 0 , 0p , time ) } 352 bool wait( condition_variable(L) & this, uintptr_t info, Time time ) with(this) { WAIT_TIME( info, 0p , time ) } 353 bool wait( condition_variable(L) & this, L & l, Duration duration ) with(this) { WAIT_TIME( 0 , &l , __kernel_get_time() + duration ) } 354 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) { WAIT_TIME( info, &l , __kernel_get_time() + duration ) } 355 bool wait( condition_variable(L) & this, L & l, Time time ) with(this) { WAIT_TIME( 0 , &l , time ) } 356 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) { WAIT_TIME( info, &l , time ) } 357 } -
libcfa/src/concurrency/locks.hfa
r42f6e07 r2b4daf2 3 3 #include <stdbool.h> 4 4 5 #include "bits/algorithm.hfa"6 5 #include "bits/locks.hfa" 7 6 #include "bits/sequence.hfa" 8 #include "bits/containers.hfa"9 7 10 8 #include "invoke.h" … … 12 10 #include "time_t.hfa" 13 11 #include "time.hfa" 14 #include <sys/time.h>15 #include "alarm.hfa"16 12 17 /////////////////////////////////////////////////////////////////// 18 //// is_blocking_lock 19 /////////////////////////////////////////////////////////////////// 13 //----------------------------------------------------------------------------- 14 // is_blocking_lock 15 trait is_blocking_lock(dtype L | sized(L)) { 16 // For synchronization locks to use when acquiring 17 void on_notify( L &, struct $thread * ); 20 18 21 trait is_blocking_lock(dtype L | sized(L)) { 22 void add_( L &, struct $thread * ); // For synchronization locks to use when acquiring 23 void remove_( L & ); // For synchronization locks to use when releasing 24 size_t get_recursion_count( L & ); // to get recursion count for cond lock to reset after waking 25 void set_recursion_count( L &, size_t recursion ); // to set recursion count after getting signalled; 19 // For synchronization locks to use when releasing 20 void on_wait( L & ); 21 22 // to get recursion count for cond lock to reset after waking 23 size_t get_recursion_count( L & ); 24 25 // to set recursion count after getting signalled; 26 void set_recursion_count( L &, size_t recursion ); 26 27 }; 27 28 28 /////////////////////////////////////////////////////////////////// 29 //// info_thread 30 /////////////////////////////////////////////////////////////////// 29 //----------------------------------------------------------------------------- 30 // info_thread 31 // the info thread is a wrapper around a thread used 32 // to store extra data for use in the condition variable 33 forall(dtype L | is_blocking_lock(L)) { 34 struct info_thread; 31 35 32 forall(dtype L | is_blocking_lock(L)) { 33 struct info_thread { 34 inline Seqable; 35 struct $thread * t; 36 uintptr_t info; 37 L * lock; 38 bool listed; // true if info_thread is on queue, false otherwise; 39 }; 40 41 42 void ?{}( info_thread(L) & this, $thread * t ); 43 void ?{}( info_thread(L) & this, $thread * t, uintptr_t info ); 44 void ^?{}( info_thread(L) & this ); 36 // for use by sequence 37 info_thread(L) *& Back( info_thread(L) * this ); 38 info_thread(L) *& Next( info_thread(L) * this ); 45 39 } 46 40 47 /////////////////////////////////////////////////////////////////// 48 //// Blocking Locks 49 /////////////////////////////////////////////////////////////////// 50 51 // struct lock_thread { 52 // struct $thread * t; 53 // lock_thread * next; 54 // }; 55 56 // void ?{}( lock_thread & this, struct $thread * thd ); 57 // void ^?{}( lock_thread & this ); 58 59 // lock_thread *& get_next( lock_thread & ); 60 41 //----------------------------------------------------------------------------- 42 // Blocking Locks 61 43 struct blocking_lock { 62 44 // Spin lock used for mutual exclusion … … 64 46 65 47 // List of blocked threads 66 __queue_t( $thread ) blocked_threads;48 Sequence( $thread ) blocked_threads; 67 49 68 50 // Count of current blocked threads … … 94 76 }; 95 77 96 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner );78 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ); 97 79 void ^?{}( blocking_lock & this ); 98 80 99 void ?{}( single_acquisition_lock & this );81 void ?{}( single_acquisition_lock & this ); 100 82 void ^?{}( single_acquisition_lock & this ); 101 83 102 void ?{}( owner_lock & this );84 void ?{}( owner_lock & this ); 103 85 void ^?{}( owner_lock & this ); 104 86 105 void ?{}( multiple_acquisition_lock & this );87 void ?{}( multiple_acquisition_lock & this ); 106 88 void ^?{}( multiple_acquisition_lock & this ); 107 89 … … 109 91 bool try_lock( blocking_lock & this ); 110 92 void unlock( blocking_lock & this ); 111 void add_( blocking_lock & this, struct $thread * t );112 void remove_( blocking_lock & this );93 void on_notify( blocking_lock & this, struct $thread * t ); 94 void on_wait( blocking_lock & this ); 113 95 size_t wait_count( blocking_lock & this ); 114 96 void set_recursion_count( blocking_lock & this, size_t recursion ); … … 117 99 void lock( single_acquisition_lock & this ); 118 100 void unlock( single_acquisition_lock & this ); 119 void add_( single_acquisition_lock & this, struct $thread * t );120 void remove_( single_acquisition_lock & this );101 void on_notify( single_acquisition_lock & this, struct $thread * t ); 102 void on_wait( single_acquisition_lock & this ); 121 103 void set_recursion_count( single_acquisition_lock & this, size_t recursion ); 122 104 size_t get_recursion_count( single_acquisition_lock & this ); … … 124 106 void lock( owner_lock & this ); 125 107 void unlock( owner_lock & this ); 126 void add_( owner_lock & this, struct $thread * t );127 void remove_( owner_lock & this );108 void on_notify( owner_lock & this, struct $thread * t ); 109 void on_wait( owner_lock & this ); 128 110 void set_recursion_count( owner_lock & this, size_t recursion ); 129 111 size_t get_recursion_count( owner_lock & this ); … … 131 113 void lock( multiple_acquisition_lock & this ); 132 114 void unlock( multiple_acquisition_lock & this ); 133 void add_( multiple_acquisition_lock & this, struct $thread * t );134 void remove_( multiple_acquisition_lock & this );115 void on_notify( multiple_acquisition_lock & this, struct $thread * t ); 116 void on_wait( multiple_acquisition_lock & this ); 135 117 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ); 136 118 size_t get_recursion_count( multiple_acquisition_lock & this ); 137 119 138 /////////////////////////////////////////////////////////////////// 139 //// Synchronization Locks 140 /////////////////////////////////////////////////////////////////// 120 //----------------------------------------------------------------------------- 121 // Synchronization Locks 141 122 forall(dtype L | is_blocking_lock(L)) { 142 123 struct condition_variable { 143 124 // Spin lock used for mutual exclusion 144 125 __spinlock_t lock; 145 146 info_thread(L) * last_thread;147 126 148 127 // List of blocked threads … … 153 132 }; 154 133 155 void ?{}( condition_variable(L) & this );134 void ?{}( condition_variable(L) & this ); 156 135 void ^?{}( condition_variable(L) & this ); 157 158 struct alarm_node_wrap {159 alarm_node_t alarm_node;160 161 condition_variable(L) * cond;162 163 info_thread(L) * i;164 };165 166 void ?{}( alarm_node_wrap(L) & this, Time alarm, Duration period, Alarm_Callback callback );167 void ^?{}( alarm_node_wrap(L) & this );168 169 void alarm_node_callback( alarm_node_wrap(L) & this );170 171 void alarm_node_wrap_cast( alarm_node_t & a );172 136 173 137 bool notify_one( condition_variable(L) & this ); … … 176 140 uintptr_t front( condition_variable(L) & this ); 177 141 178 bool empty ( condition_variable(L) & this );179 int counter( condition_variable(L) & this );142 bool empty ( condition_variable(L) & this ); 143 int counter( condition_variable(L) & this ); 180 144 181 // TODO: look into changing timout routines to return bool showing if signalled or woken by kernel182 145 void wait( condition_variable(L) & this ); 183 146 void wait( condition_variable(L) & this, uintptr_t info ); 184 voidwait( condition_variable(L) & this, Duration duration );185 voidwait( condition_variable(L) & this, uintptr_t info, Duration duration );186 voidwait( condition_variable(L) & this, Time time );187 voidwait( condition_variable(L) & this, uintptr_t info, Time time );147 bool wait( condition_variable(L) & this, Duration duration ); 148 bool wait( condition_variable(L) & this, uintptr_t info, Duration duration ); 149 bool wait( condition_variable(L) & this, Time time ); 150 bool wait( condition_variable(L) & this, uintptr_t info, Time time ); 188 151 189 152 void wait( condition_variable(L) & this, L & l ); 190 153 void wait( condition_variable(L) & this, L & l, uintptr_t info ); 191 voidwait( condition_variable(L) & this, L & l, Duration duration );192 voidwait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration );193 voidwait( condition_variable(L) & this, L & l, Time time );194 voidwait( condition_variable(L) & this, L & l, uintptr_t info, Time time );154 bool wait( condition_variable(L) & this, L & l, Duration duration ); 155 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ); 156 bool wait( condition_variable(L) & this, L & l, Time time ); 157 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ); 195 158 } -
libcfa/src/concurrency/monitor.hfa
r42f6e07 r2b4daf2 132 132 133 133 void wait ( condition & this, uintptr_t user_info = 0 ); 134 static inline bool is_empty ( condition & this ) { return this.blocked.head == 1p; } 134 135 bool signal ( condition & this ); 135 136 bool signal_block( condition & this ); 136 static inline bool is_empty ( condition & this ) { return this.blocked.head == 1p; }137 static inline bool signal_all ( condition & this ) { bool ret = false; while(!is_empty(this)) { ret = signal(this) || ret; } return ret; } 137 138 uintptr_t front ( condition & this ); 138 139 -
libcfa/src/concurrency/thread.cfa
r42f6e07 r2b4daf2 43 43 canary = 0x0D15EA5E0D15EA5Ep; 44 44 #endif 45 46 seqable.next = 0p; 47 seqable.back = 0p; 45 48 46 49 node.next = 0p; … … 130 133 131 134 this_thrd->context.[SP, FP] = this_thrd->self_cor.context.[SP, FP]; 132 verify( this_thrd->context.SP );135 /* paranoid */ verify( this_thrd->context.SP ); 133 136 134 137 __schedule_thread( this_thrd ); -
libcfa/src/heap.cfa
r42f6e07 r2b4daf2 10 10 // Created On : Tue Dec 19 21:58:35 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Dec 15 21:37:54202013 // Update Count : 10 1312 // Last Modified On : Wed Dec 16 12:28:25 2020 13 // Update Count : 1023 14 14 // 15 15 … … 438 438 header = headerAddr( addr ); 439 439 440 if ( unlikely( heapEnd < addr ) ) {// mmapped ?440 if ( unlikely( addr < heapBegin || heapEnd < addr ) ) { // mmapped ? 441 441 fakeHeader( header, alignment ); 442 442 size = header->kind.real.blockSize & -3; // mmap size … … 445 445 446 446 #ifdef __CFA_DEBUG__ 447 checkHeader( addr < heapBegin, name, addr );// bad low address ?447 checkHeader( header < (HeapManager.Storage.Header *)heapBegin, name, addr ); // bad low address ? 448 448 #endif // __CFA_DEBUG__ 449 449 … … 484 484 #endif // __CFA_DEBUG__ 485 485 486 486 487 #define NO_MEMORY_MSG "insufficient heap memory available for allocating %zd new bytes." 487 488 488 #include <unistd.h>489 489 static inline void * extend( size_t size ) with( heapManager ) { 490 490 lock( extlock __cfaabi_dbg_ctx2 ); … … 511 511 #ifdef __CFA_DEBUG__ 512 512 // Set new memory to garbage so subsequent uninitialized usages might fail. 513 memset( (char *)heapEnd + heapRemaining, '\ hde', increase );513 memset( (char *)heapEnd + heapRemaining, '\xde', increase ); 514 514 //Memset( (char *)heapEnd + heapRemaining, increase ); 515 515 #endif // __CFA_DEBUG__ … … 586 586 #ifdef __CFA_DEBUG__ 587 587 // Set new memory to garbage so subsequent uninitialized usages might fail. 588 memset( block, '\ hde', tsize );588 memset( block, '\xde', tsize ); 589 589 //Memset( block, tsize ); 590 590 #endif // __CFA_DEBUG__ … … 634 634 #ifdef __CFA_DEBUG__ 635 635 // Set free memory to garbage so subsequent usages might fail. 636 memset( ((HeapManager.Storage *)header)->data, '\ hde', freeElem->blockSize - sizeof( HeapManager.Storage ) );636 memset( ((HeapManager.Storage *)header)->data, '\xde', freeElem->blockSize - sizeof( HeapManager.Storage ) ); 637 637 //Memset( ((HeapManager.Storage *)header)->data, freeElem->blockSize - sizeof( HeapManager.Storage ) ); 638 638 #endif // __CFA_DEBUG__ … … 1029 1029 } // cmemalign 1030 1030 1031 1031 1032 // Same as memalign(), but ISO/IEC 2011 C11 Section 7.22.2 states: the value of size shall be an integral multiple 1032 1033 // of alignment. This requirement is universally ignored. … … 1045 1046 return 0; 1046 1047 } // posix_memalign 1048 1047 1049 1048 1050 // Allocates size bytes and returns a pointer to the allocated memory. The memory address shall be a multiple of the -
libcfa/src/memory.cfa
r42f6e07 r2b4daf2 66 66 forall(dtype T | sized(T), ttype Args | { void ?{}(T&, Args); }) 67 67 void ?{}(counter_ptr(T) & this, Args args) { 68 this.data = new(args);68 this.data = (counter_data(T)*)new(args); 69 69 } 70 70 … … 126 126 forall(dtype T | sized(T), ttype Args | { void ?{}(T &, Args); }) 127 127 void ?{}(unique_ptr(T) & this, Args args) { 128 this.data = new(args);128 this.data = (T *)new(args); 129 129 } 130 130 -
libcfa/src/parseargs.cfa
r42f6e07 r2b4daf2 105 105 if(opt == options[i].short_name) { 106 106 const char * arg = optarg ? optarg : ""; 107 if( arg[0] == '=' ) { arg++; } 107 108 bool success = options[i].parse( arg, options[i].variable ); 108 109 if(success) continue NEXT_ARG; … … 185 186 } 186 187 188 bool parse_truefalse(const char * arg, bool & value) { 189 if(strcmp(arg, "true") == 0) { 190 value = true; 191 return true; 192 } 193 194 if(strcmp(arg, "false") == 0) { 195 value = false; 196 return true; 197 } 198 199 return false; 200 } 201 187 202 bool parse_settrue (const char *, bool & value ) { 188 203 value = true; -
libcfa/src/parseargs.hfa
r42f6e07 r2b4daf2 37 37 void print_args_usage(int argc, char * argv[], cfa_option options[], size_t opt_count, const char * usage, bool error) __attribute__ ((noreturn)); 38 38 39 bool parse_yesno (const char *, bool & ); 40 bool parse_settrue (const char *, bool & ); 41 bool parse_setfalse(const char *, bool & ); 39 bool parse_yesno (const char *, bool & ); 40 bool parse_truefalse(const char *, bool & ); 41 bool parse_settrue (const char *, bool & ); 42 bool parse_setfalse (const char *, bool & ); 42 43 43 44 bool parse(const char *, const char * & ); -
libcfa/src/stdlib.hfa
r42f6e07 r2b4daf2 267 267 static inline forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } ) 268 268 T * new( TT p ) { 269 return &(* malloc()){ p }; // run constructor269 return &(*(T *)malloc()){ p }; // run constructor 270 270 } // new 271 271 -
longrun_tests/Makefile.am
r42f6e07 r2b4daf2 44 44 -DTEST_$(shell cat .type | tr a-z A-Z) 45 45 46 TESTS = block coroutine create disjoint enter enter3 processor stack wait yield46 TESTS = block coroutine create disjoint enter enter3 locks processor stack wait yield 47 47 48 48 # .INTERMEDIATE: $(TESTS) -
src/AST/Convert.cpp
r42f6e07 r2b4daf2 205 205 ftype->parameters = get<DeclarationWithType>().acceptL(node->params); 206 206 207 ftype->forall = get<TypeDecl>().acceptL( node->type->forall ); 207 ftype->forall = get<TypeDecl>().acceptL( node->type_params ); 208 if (!node->assertions.empty()) { 209 assert(!ftype->forall.empty()); 210 // find somewhere to place assertions back, for convenience it is the last slot 211 ftype->forall.back()->assertions = get<DeclarationWithType>().acceptL(node->assertions); 212 } 208 213 209 214 visitType(node->type, ftype); … … 602 607 603 608 for (decltype(src->begin()) src_i = src->begin(); src_i != src->end(); src_i++) { 604 rslt->add( src_i->first ,609 rslt->add( src_i->first.typeString(), 605 610 get<Type>().accept1(src_i->second) ); 606 }607 608 for (decltype(src->beginVar()) src_i = src->beginVar(); src_i != src->endVar(); src_i++) {609 rslt->addVar( src_i->first,610 get<Expression>().accept1(src_i->second) );611 611 } 612 612 … … 1212 1212 // ty->returnVals = get<DeclarationWithType>().acceptL( node->returns ); 1213 1213 // ty->parameters = get<DeclarationWithType>().acceptL( node->params ); 1214 ty->forall = get<TypeDecl>().acceptL( node->forall ); 1214 1215 auto types = get<TypeInstType>().acceptL( node->forall ); 1216 for (auto t : types) { 1217 auto newT = new TypeDecl(*t->baseType); 1218 newT->name = t->name; // converted by typeString() 1219 for (auto asst : newT->assertions) delete asst; 1220 newT->assertions.clear(); 1221 ty->forall.push_back(newT); 1222 } 1223 auto assts = get<VariableExpr>().acceptL( node->assertions ); 1224 if (!assts.empty()) { 1225 assert(!types.empty()); 1226 for (auto asst : assts) { 1227 auto newDecl = new ObjectDecl(*strict_dynamic_cast<ObjectDecl*>(asst->var)); 1228 delete newDecl->type; 1229 newDecl->type = asst->result->clone(); 1230 newDecl->storageClasses.is_extern = true; // hack 1231 ty->forall.back()->assertions.push_back(newDecl); 1232 } 1233 } 1234 1215 1235 return visitType( node, ty ); 1216 1236 } … … 1299 1319 ty = new TypeInstType{ 1300 1320 cv( node ), 1301 node-> name,1321 node->typeString(), 1302 1322 get<TypeDecl>().accept1( node->base ), 1303 1323 get<Attribute>().acceptL( node->attributes ) … … 1306 1326 ty = new TypeInstType{ 1307 1327 cv( node ), 1308 node-> name,1328 node->typeString(), 1309 1329 node->kind == ast::TypeDecl::Ftype, 1310 1330 get<Attribute>().acceptL( node->attributes ) … … 1431 1451 /// at conversion stage, all created nodes are guaranteed to be unique, therefore 1432 1452 /// const_casting out of smart pointers is permitted. 1433 std::unordered_map< const BaseSyntaxNode *, ast:: ptr<ast::Node> > cache = {};1453 std::unordered_map< const BaseSyntaxNode *, ast::readonly<ast::Node> > cache = {}; 1434 1454 1435 1455 // Local Utilities: … … 1565 1585 // can function type have attributes? seems not to be the case. 1566 1586 // visitType(old->type, ftype); 1587 1588 // collect assertions and put directly in FunctionDecl 1589 std::vector<ast::ptr<ast::DeclWithType>> assertions; 1590 for (auto & param: forall) { 1591 for (auto & asst: param->assertions) { 1592 assertf(asst->unique(), "newly converted decl must be unique"); 1593 assertions.emplace_back(asst); 1594 } 1595 auto mut = param.get_and_mutate(); 1596 assertf(mut == param, "newly converted decl must be unique"); 1597 mut->assertions.clear(); 1598 } 1567 1599 1568 1600 auto decl = new ast::FunctionDecl{ … … 1584 1616 cache.emplace( old, decl ); 1585 1617 1618 decl->assertions = std::move(assertions); 1586 1619 decl->withExprs = GET_ACCEPT_V(withExprs, Expr); 1587 1620 decl->stmts = GET_ACCEPT_1(statements, CompoundStmt); … … 2066 2099 } 2067 2100 2101 // TypeSubstitution shouldn't exist yet in old. 2068 2102 ast::TypeSubstitution * convertTypeSubstitution(const TypeSubstitution * old) { 2069 2103 2070 2104 if (!old) return nullptr; 2071 2105 if (old->empty()) return nullptr; 2106 assert(false); 2107 2108 /* 2072 2109 ast::TypeSubstitution *rslt = new ast::TypeSubstitution(); 2073 2110 … … 2077 2114 } 2078 2115 2079 for (decltype(old->beginVar()) old_i = old->beginVar(); old_i != old->endVar(); old_i++) {2080 rslt->addVar( old_i->first,2081 getAccept1<ast::Expr>(old_i->second) );2082 }2083 2084 2116 return rslt; 2117 */ 2085 2118 } 2086 2119 … … 2610 2643 ty->params.emplace_back(v->get_type()); 2611 2644 } 2612 ty->forall = GET_ACCEPT_V( forall, TypeDecl ); 2645 // xxx - when will this be non-null? 2646 // will have to create dangling (no-owner) decls to be pointed to 2647 auto foralls = GET_ACCEPT_V( forall, TypeDecl ); 2648 2649 for (auto & param : foralls) { 2650 ty->forall.emplace_back(new ast::TypeInstType(param->name, param)); 2651 for (auto asst : param->assertions) { 2652 ty->assertions.emplace_back(new ast::VariableExpr({}, asst)); 2653 } 2654 } 2613 2655 visitType( old, ty ); 2614 2656 } -
src/AST/Decl.cpp
r42f6e07 r2b4daf2 50 50 51 51 FunctionDecl::FunctionDecl( const CodeLocation & loc, const std::string & name, 52 std::vector<ptr<TypeDecl>>&& forall, 53 std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns, 54 CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage, 55 std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs) 56 : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)), 57 stmts( stmts ) { 58 FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs)); 59 for (auto & param : this->params) { 60 ftype->params.emplace_back(param->get_type()); 61 } 62 for (auto & ret : this->returns) { 63 ftype->returns.emplace_back(ret->get_type()); 64 } 65 ftype->forall = std::move(forall); 66 this->type = ftype; 67 } 52 std::vector<ptr<TypeDecl>>&& forall, 53 std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns, 54 CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage, 55 std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs) 56 : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)), 57 type_params(std::move(forall)), stmts( stmts ) { 58 FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs)); 59 for (auto & param : this->params) { 60 ftype->params.emplace_back(param->get_type()); 61 } 62 for (auto & ret : this->returns) { 63 ftype->returns.emplace_back(ret->get_type()); 64 } 65 for (auto & tp : this->type_params) { 66 ftype->forall.emplace_back(new TypeInstType(tp->name, tp)); 67 } 68 this->type = ftype; 69 } 68 70 69 71 -
src/AST/Decl.hpp
r42f6e07 r2b4daf2 127 127 std::vector<ptr<DeclWithType>> params; 128 128 std::vector<ptr<DeclWithType>> returns; 129 std::vector<ptr<TypeDecl>> type_params; 130 std::vector<ptr<DeclWithType>> assertions; 129 131 // declared type, derived from parameter declarations 130 132 ptr<FunctionType> type; 131 133 ptr<CompoundStmt> stmts; 132 134 std::vector< ptr<Expr> > withExprs; 135 133 136 134 137 FunctionDecl( const CodeLocation & loc, const std::string & name, std::vector<ptr<TypeDecl>>&& forall, -
src/AST/Expr.cpp
r42f6e07 r2b4daf2 206 206 assert( aggregate->result ); 207 207 208 // Deep copy on result type avoids mutation on transitively multiply referenced object. 209 // 210 // Example, adapted from parts of builtins and bootloader: 211 // 212 // forall(dtype T) 213 // struct __Destructor { 214 // T * object; 215 // void (*dtor)(T *); 216 // }; 217 // 218 // forall(dtype S) 219 // void foo(__Destructor(S) &d) { 220 // if (d.dtor) { // here 221 // } 222 // } 223 // 224 // Let e be the "d.dtor" guard espression, which is MemberExpr after resolve. Let d be the 225 // declaration of member __Destructor.dtor (an ObjectDecl), as accessed via the top-level 226 // declaration of __Destructor. Consider the types e.result and d.type. In the old AST, one 227 // is a clone of the other. Ordinary new-AST use would set them up as a multiply-referenced 228 // object. 229 // 230 // e.result: PointerType 231 // .base: FunctionType 232 // .params.front(): ObjectDecl, the anonymous parameter of type T* 233 // .type: PointerType 234 // .base: TypeInstType 235 // let x = that 236 // let y = similar, except start from d.type 237 // 238 // Consider two code lines down, genericSubstitution(...).apply(result). 239 // 240 // Applying this chosen-candidate's type substitution means modifying x, substituting 241 // S for T. This mutation should affect x and not y. 242 243 result = deepCopy(mem->get_type()); 208 result = mem->get_type(); 244 209 245 210 // substitute aggregate generic parameters into member type -
src/AST/Node.hpp
r42f6e07 r2b4daf2 229 229 const node_t & operator* () const { _check(); return *node; } 230 230 explicit operator bool() const { _check(); return node; } 231 operator const node_t * () const { _check(); return node; } 231 operator const node_t * () const & { _check(); return node; } 232 operator const node_t * () && = delete; 232 233 233 234 const node_t * release() { -
src/AST/Pass.hpp
r42f6e07 r2b4daf2 34 34 35 35 #include "AST/SymbolTable.hpp" 36 37 #include "AST/ForallSubstitutionTable.hpp"38 36 39 37 // Private prelude header, needed for some of the magic tricks this class pulls off … … 66 64 // | WithVisitorRef - provides an pointer to the templated visitor wrapper 67 65 // | WithSymbolTable - provides symbol table functionality 68 // | WithForallSubstitutor - maintains links between TypeInstType and TypeDecl under mutation69 66 // 70 67 // Other Special Members: … … 258 255 container_t< ptr<node_t> > call_accept( const container_t< ptr<node_t> > & container ); 259 256 260 /// Mutate forall-list, accounting for presence of type substitution map261 template<typename node_t>262 void mutate_forall( const node_t *& );263 264 257 public: 265 258 /// Logic to call the accept and mutate the parent if needed, delegates call to accept … … 398 391 }; 399 392 400 /// Use when the templated visitor needs to keep TypeInstType instances properly linked to TypeDecl401 struct WithForallSubstitutor {402 ForallSubstitutionTable subs;403 };404 405 393 } 406 394 -
src/AST/Pass.impl.hpp
r42f6e07 r2b4daf2 367 367 } 368 368 369 370 template< typename core_t >371 template< typename node_t >372 void ast::Pass< core_t >::mutate_forall( const node_t *& node ) {373 if ( auto subs = __pass::forall::subs( core, 0 ) ) {374 // tracking TypeDecl substitution, full clone375 if ( node->forall.empty() ) return;376 377 node_t * mut = __pass::mutate<core_t>( node );378 mut->forall = subs->clone( node->forall, *this );379 node = mut;380 } else {381 // not tracking TypeDecl substitution, just mutate382 maybe_accept( node, &node_t::forall );383 }384 }385 369 } 386 370 … … 504 488 __pass::symtab::addId( core, 0, func ); 505 489 VISIT( 506 // parameter declarations are now directly here490 // parameter declarations 507 491 maybe_accept( node, &FunctionDecl::params ); 508 492 maybe_accept( node, &FunctionDecl::returns ); 509 // foralls are still in function type 510 maybe_accept( node, &FunctionDecl::type ); 493 // type params and assertions 494 maybe_accept( node, &FunctionDecl::type_params ); 495 maybe_accept( node, &FunctionDecl::assertions ); 511 496 // First remember that we are now within a function. 512 497 ValueGuard< bool > oldInFunction( inFunction ); … … 1758 1743 1759 1744 VISIT({ 1760 guard_forall_subs forall_guard { *this, node }; 1761 mutate_forall( node ); 1745 // guard_forall_subs forall_guard { *this, node }; 1746 // mutate_forall( node ); 1747 maybe_accept( node, &FunctionType::assertions ); 1762 1748 maybe_accept( node, &FunctionType::returns ); 1763 1749 maybe_accept( node, &FunctionType::params ); … … 1981 1967 { 1982 1968 bool mutated = false; 1983 std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;1969 std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map; 1984 1970 for ( const auto & p : node->typeEnv ) { 1985 1971 guard_symtab guard { *this }; … … 1994 1980 } 1995 1981 } 1996 1997 {1998 bool mutated = false;1999 std::unordered_map< std::string, ast::ptr< ast::Expr > > new_map;2000 for ( const auto & p : node->varEnv ) {2001 guard_symtab guard { *this };2002 auto new_node = p.second->accept( *this );2003 if (new_node != p.second) mutated = true;2004 new_map.insert({ p.first, new_node });2005 }2006 if (mutated) {2007 auto new_node = __pass::mutate<core_t>( node );2008 new_node->varEnv.swap( new_map );2009 node = new_node;2010 }2011 }2012 1982 ) 2013 1983 -
src/AST/Pass.proto.hpp
r42f6e07 r2b4daf2 413 413 static inline auto leave( core_t &, long, const ast::FunctionType * ) {} 414 414 415 // Get the substitution table, if present416 template<typename core_t>417 static inline auto subs( core_t & core, int ) -> decltype( &core.subs ) {418 return &core.subs;419 }420 421 template<typename core_t>422 static inline ast::ForallSubstitutionTable * subs( core_t &, long ) { return nullptr; }423 424 415 // Replaces a TypeInstType's base TypeDecl according to the table 425 416 template<typename core_t> -
src/AST/Print.cpp
r42f6e07 r2b4daf2 155 155 } 156 156 157 void print( const ast::FunctionType::AssertionList & assts ) { 158 if (assts.empty()) return; 159 os << "with assertions" << endl; 160 ++indent; 161 printAll(assts); 162 os << indent; 163 --indent; 164 } 165 157 166 void print( const std::vector<ptr<Attribute>> & attrs ) { 158 167 if ( attrs.empty() ) return; … … 206 215 void preprint( const ast::NamedTypeDecl * node ) { 207 216 if ( ! node->name.empty() ) { 208 if( deterministic_output && isUnboundType(node->name) ) os << "[unbound]:"; 209 else os << node->name << ": "; 217 os << node->name << ": "; 210 218 } 211 219 … … 261 269 void preprint( const ast::FunctionType * node ) { 262 270 print( node->forall ); 271 print( node->assertions ); 263 272 print( node->qualifiers ); 264 273 } … … 1375 1384 virtual const ast::Type * visit( const ast::TypeInstType * node ) override final { 1376 1385 preprint( node ); 1377 const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node-> name;1386 const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->typeString(); 1378 1387 os << "instance of type " << _name 1379 1388 << " (" << (node->kind == ast::TypeDecl::Ftype ? "" : "not ") << "function type)"; … … 1502 1511 os << indent << "Types:" << endl; 1503 1512 for ( const auto& i : *node ) { 1504 os << indent+1 << i.first << " -> ";1513 os << indent+1 << i.first.typeString() << " -> "; 1505 1514 indent += 2; 1506 1515 safe_print( i.second ); 1507 indent -= 2;1508 os << endl;1509 }1510 os << indent << "Non-types:" << endl;1511 for ( auto i = node->beginVar(); i != node->endVar(); ++i ) {1512 os << indent+1 << i->first << " -> ";1513 indent += 2;1514 safe_print( i->second );1515 1516 indent -= 2; 1516 1517 os << endl; -
src/AST/SymbolTable.cpp
r42f6e07 r2b4daf2 414 414 415 415 void SymbolTable::addFunction( const FunctionDecl * func ) { 416 addTypes( func->type->forall ); 416 for (auto & td : func->type_params) { 417 addType(td); 418 } 419 for (auto & asst : func->assertions) { 420 addId(asst); 421 } 422 // addTypes( func->type->forall ); 417 423 addIds( func->returns ); 418 424 addIds( func->params ); -
src/AST/Type.cpp
r42f6e07 r2b4daf2 21 21 22 22 #include "Decl.hpp" 23 #include "ForallSubstitutor.hpp" // for substituteForall24 23 #include "Init.hpp" 25 24 #include "Common/utility.h" // for copy, move … … 92 91 // GENERATED END 93 92 94 // --- ParameterizedType95 96 void FunctionType::initWithSub(97 const FunctionType & o, Pass< ForallSubstitutor > & sub98 ) {99 forall = sub.core( o.forall );100 }101 102 93 // --- FunctionType 103 104 105 FunctionType::FunctionType( const FunctionType & o )106 : Type( o.qualifiers, copy( o.attributes ) ), returns(), params(),107 isVarArgs( o.isVarArgs ) {108 Pass< ForallSubstitutor > sub;109 initWithSub( o, sub ); // initialize substitution map110 returns = sub.core( o.returns ); // apply to return and parameter types111 params = sub.core( o.params );112 }113 114 94 namespace { 115 95 bool containsTtype( const std::vector<ptr<Type>> & l ) { … … 199 179 // TODO: once TypeInstType representation is updated, it should properly check 200 180 // if the context id is filled. this is a temporary hack for now 201 return isUnboundType(typeInst->name); 202 } 203 return false; 204 } 205 206 bool isUnboundType(const std::string & tname) { 207 // xxx - look for a type name produced by renameTyVars. 208 209 // TODO: once TypeInstType representation is updated, it should properly check 210 // if the context id is filled. this is a temporary hack for now 211 if (std::count(tname.begin(), tname.end(), '_') >= 3) { 212 return true; 181 return typeInst->formal_usage > 0; 213 182 } 214 183 return false; -
src/AST/Type.hpp
r42f6e07 r2b4daf2 36 36 37 37 template< typename T > class Pass; 38 39 struct ForallSubstitutor;40 38 41 39 class Type : public Node { … … 272 270 /// Type of a function `[R1, R2](*)(P1, P2, P3)` 273 271 class FunctionType final : public Type { 274 protected: 275 /// initializes forall with substitutor 276 void initWithSub( const FunctionType & o, Pass< ForallSubstitutor > & sub ); 277 public: 278 using ForallList = std::vector<ptr<TypeDecl>>; 272 public: 273 using ForallList = std::vector<ptr<TypeInstType>>; 274 using AssertionList = std::vector<ptr<VariableExpr>>; 279 275 ForallList forall; 276 AssertionList assertions; 280 277 281 278 std::vector<ptr<Type>> returns; … … 292 289 : Type(q), returns(), params(), isVarArgs(va) {} 293 290 294 FunctionType( const FunctionType & o ) ;291 FunctionType( const FunctionType & o ) = default; 295 292 296 293 /// true if either the parameters or return values contain a tttype … … 397 394 public: 398 395 readonly<TypeDecl> base; 396 // previously from renameTyVars; now directly use integer fields instead of synthesized strings 397 // a nonzero value of formal_usage indicates a formal type (only used in function type) 398 // a zero value of formal_usage indicates an actual type (referenced inside body of parametric structs and functions) 399 399 TypeDecl::Kind kind; 400 int formal_usage = 0; 401 int expr_id = 0; 402 403 // compact representation used for map lookups. 404 struct TypeEnvKey { 405 const TypeDecl * base; 406 int formal_usage; 407 int expr_id; 408 409 TypeEnvKey() = default; 410 TypeEnvKey(const TypeDecl * base, int formal_usage = 0, int expr_id = 0): base(base), formal_usage(formal_usage), expr_id(expr_id) {} 411 TypeEnvKey(const TypeInstType & inst): base(inst.base), formal_usage(inst.formal_usage), expr_id(inst.expr_id) {} 412 std::string typeString() const { return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + base->name; } 413 bool operator==(const TypeEnvKey & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; } 414 415 }; 416 417 bool operator==(const TypeInstType & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; } 400 418 401 419 TypeInstType( … … 409 427 TypeInstType( const TypeInstType & o ) = default; 410 428 429 TypeInstType( const TypeEnvKey & key ) 430 : BaseInstType(key.base->name), base(key.base), kind(key.base->kind), formal_usage(key.formal_usage), expr_id(key.expr_id) {} 431 411 432 /// sets `base`, updating `kind` correctly 412 433 void set_base( const TypeDecl * ); … … 418 439 419 440 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 441 442 std::string typeString() const { 443 if (formal_usage > 0) return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + name; 444 else return name; 445 } 420 446 private: 421 447 TypeInstType * clone() const override { return new TypeInstType{ *this }; } … … 510 536 511 537 bool isUnboundType(const Type * type); 512 bool isUnboundType(const std::string & tname); 513 538 539 } 540 541 namespace std { 542 template<> 543 struct hash<typename ast::TypeInstType::TypeEnvKey> { 544 size_t operator() (const ast::TypeInstType::TypeEnvKey & x) const { 545 const size_t p = 1000007; 546 size_t res = reinterpret_cast<size_t>(x.base); 547 res = p * res + x.formal_usage; 548 res = p * res + x.expr_id; 549 return res; 550 } 551 }; 514 552 } 515 553 -
src/AST/TypeEnvironment.cpp
r42f6e07 r2b4daf2 52 52 for ( const auto & i : open ) { 53 53 if ( first ) { first = false; } else { out << ' '; } 54 out << i.first << "(" << i.second << ")";54 out << i.first.typeString() << "(" << i.second << ")"; 55 55 } 56 56 } … … 62 62 if(first) first = false; 63 63 else out << " "; 64 if( deterministic_output && isUnboundType(var) ) out << "[unbound]"; 65 else out << var; 64 65 if( deterministic_output ) out << "[unbound]"; 66 else out << "_" << var.formal_usage << "_" << var.expr_id << "_"; 67 68 out << var.base->name; 66 69 } 67 70 out << ")"; … … 79 82 } 80 83 81 const EqvClass * TypeEnvironment::lookup( const std::string& var ) const {84 const EqvClass * TypeEnvironment::lookup( const TypeInstType::TypeEnvKey & var ) const { 82 85 for ( ClassList::const_iterator i = env.begin(); i != env.end(); ++i ) { 83 86 if ( i->vars.find( var ) != i->vars.end() ) return &*i; … … 106 109 107 110 void TypeEnvironment::add( const FunctionType::ForallList & tyDecls ) { 108 for ( const TypeDecl *tyDecl : tyDecls ) {111 for ( auto & tyDecl : tyDecls ) { 109 112 env.emplace_back( tyDecl ); 110 113 } … … 119 122 void TypeEnvironment::writeToSubstitution( TypeSubstitution & sub ) const { 120 123 for ( const auto & clz : env ) { 121 std::string clzRep; 124 TypeInstType::TypeEnvKey clzRep; 125 bool first = true; 122 126 for ( const auto & var : clz.vars ) { 123 127 if ( clz.bound ) { 124 128 sub.add( var, clz.bound ); 125 } else if ( clzRep.empty()) {129 } else if ( first ) { 126 130 clzRep = var; 131 first = false; 127 132 } else { 128 sub.add( var, new TypeInstType{ clzRep , clz.data.kind} );133 sub.add( var, new TypeInstType{ clzRep } ); 129 134 } 130 135 } … … 141 146 struct Occurs : public ast::WithVisitorRef<Occurs> { 142 147 bool result; 143 std:: set< std::string> vars;148 std::unordered_set< TypeInstType::TypeEnvKey > vars; 144 149 const TypeEnvironment & tenv; 145 150 146 Occurs( const std::string& var, const TypeEnvironment & env )151 Occurs( const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env ) 147 152 : result( false ), vars(), tenv( env ) { 148 153 if ( const EqvClass * clz = tenv.lookup( var ) ) { … … 154 159 155 160 void previsit( const TypeInstType * typeInst ) { 156 if ( vars.count( typeInst->name) ) {161 if ( vars.count( *typeInst ) ) { 157 162 result = true; 158 } else if ( const EqvClass * clz = tenv.lookup( typeInst->name) ) {163 } else if ( const EqvClass * clz = tenv.lookup( *typeInst ) ) { 159 164 if ( clz->bound ) { 160 165 clz->bound->accept( *visitor ); … … 165 170 166 171 /// true if `var` occurs in `ty` under `env` 167 bool occurs( const Type * ty, const std::string& var, const TypeEnvironment & env ) {172 bool occurs( const Type * ty, const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env ) { 168 173 Pass<Occurs> occur{ var, env }; 169 174 maybe_accept( ty, occur ); … … 280 285 // remove references from bound type, so that type variables can only bind to value types 281 286 ptr<Type> target = bindTo->stripReferences(); 282 auto tyvar = open.find( typeInst->name);287 auto tyvar = open.find( *typeInst ); 283 288 assert( tyvar != open.end() ); 284 289 if ( ! tyVarCompatible( tyvar->second, target ) ) return false; 285 if ( occurs( target, typeInst->name, *this ) ) return false;286 287 auto it = internal_lookup( typeInst->name);290 if ( occurs( target, *typeInst, *this ) ) return false; 291 292 auto it = internal_lookup( *typeInst ); 288 293 if ( it != env.end() ) { 289 294 if ( it->bound ) { … … 308 313 } else { 309 314 env.emplace_back( 310 typeInst->name, target, widen.first && widen.second, data );315 *typeInst, target, widen.first && widen.second, data ); 311 316 } 312 317 return true; … … 318 323 WidenMode widen, const SymbolTable & symtab 319 324 ) { 320 auto c1 = internal_lookup( var1->name);321 auto c2 = internal_lookup( var2->name);325 auto c1 = internal_lookup( *var1 ); 326 auto c2 = internal_lookup( *var2 ); 322 327 323 328 // exit early if variables already bound together … … 333 338 if ( c1 != env.end() ) { 334 339 if ( c1->bound ) { 335 if ( occurs( c1->bound, var2->name, *this ) ) return false;340 if ( occurs( c1->bound, *var2, *this ) ) return false; 336 341 type1 = c1->bound; 337 342 } … … 340 345 if ( c2 != env.end() ) { 341 346 if ( c2->bound ) { 342 if ( occurs( c2->bound, var1->name, *this ) ) return false;347 if ( occurs( c2->bound, *var1, *this ) ) return false; 343 348 type2 = c2->bound; 344 349 } … … 378 383 } else if ( c1 != env.end() ) { 379 384 // var2 unbound, add to env[c1] 380 c1->vars.emplace( var2->name);385 c1->vars.emplace( *var2 ); 381 386 c1->allowWidening = widen1; 382 387 c1->data.isComplete |= data.isComplete; 383 388 } else if ( c2 != env.end() ) { 384 389 // var1 unbound, add to env[c2] 385 c2->vars.emplace( var1->name);390 c2->vars.emplace( *var1 ); 386 391 c2->allowWidening = widen2; 387 392 c2->data.isComplete |= data.isComplete; 388 393 } else { 389 394 // neither var bound, create new class 390 env.emplace_back( var1->name, var2->name, widen1 && widen2, data );395 env.emplace_back( *var1, *var2, widen1 && widen2, data ); 391 396 } 392 397 … … 452 457 } 453 458 454 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const std::string& var ) {459 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const TypeInstType::TypeEnvKey & var ) { 455 460 for ( ClassList::iterator i = env.begin(); i != env.end(); ++i ) { 456 461 if ( i->vars.count( var ) ) return i; -
src/AST/TypeEnvironment.hpp
r42f6e07 r2b4daf2 55 55 /// recorded. More investigation is needed. 56 56 struct AssertCompare { 57 bool operator()( const DeclWithType * d1, const DeclWithType* d2 ) const {58 int cmp = d1-> name.compare( d2->name );59 return cmp < 0 || ( cmp == 0 && d1-> get_type() < d2->get_type());57 bool operator()( const VariableExpr * d1, const VariableExpr * d2 ) const { 58 int cmp = d1->var->name.compare( d2->var->name ); 59 return cmp < 0 || ( cmp == 0 && d1->result < d2->result ); 60 60 } 61 61 }; … … 70 70 71 71 /// Set of assertions pending satisfaction 72 using AssertionSet = std::map< readonly<DeclWithType>, AssertionSetValue, AssertCompare >;72 using AssertionSet = std::map< const VariableExpr *, AssertionSetValue, AssertCompare >; 73 73 74 74 /// Set of open variables 75 using OpenVarSet = std::unordered_map< std::string, TypeDecl::Data >;75 using OpenVarSet = std::unordered_map< TypeInstType::TypeEnvKey, TypeDecl::Data >; 76 76 77 77 /// Merges one set of open vars into another … … 89 89 /// they bind to. 90 90 struct EqvClass { 91 std:: set< std::string> vars;91 std::unordered_set< TypeInstType::TypeEnvKey > vars; 92 92 ptr<Type> bound; 93 93 bool allowWidening; … … 101 101 102 102 /// Singleton class constructor from TypeDecl 103 EqvClass( const Type Decl * decl)104 : vars{ decl->name }, bound(), allowWidening( true ), data( decl) {}103 EqvClass( const TypeInstType * inst ) 104 : vars{ *inst }, bound(), allowWidening( true ), data( inst->base ) {} 105 105 106 106 /// Singleton class constructor from substitution 107 EqvClass( const std::string& v, const Type * b )107 EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b ) 108 108 : vars{ v }, bound( b ), allowWidening( false ), data( TypeDecl::Dtype, false ) {} 109 109 110 110 /// Single-var constructor (strips qualifiers from bound type) 111 EqvClass( const std::string& v, const Type * b, bool w, const TypeDecl::Data & d )111 EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b, bool w, const TypeDecl::Data & d ) 112 112 : vars{ v }, bound( b ), allowWidening( w ), data( d ) { 113 113 reset_qualifiers( bound ); … … 115 115 116 116 /// Double-var constructor 117 EqvClass( const std::string & v, const std::string& u, bool w, const TypeDecl::Data & d )117 EqvClass( const TypeInstType::TypeEnvKey & v, const TypeInstType::TypeEnvKey & u, bool w, const TypeDecl::Data & d ) 118 118 : vars{ v, u }, bound(), allowWidening( w ), data( d ) {} 119 119 … … 131 131 public: 132 132 /// Finds the equivalence class containing a variable; nullptr for none such 133 const EqvClass * lookup( const std::string& var ) const;133 const EqvClass * lookup( const TypeInstType::TypeEnvKey & var ) const; 134 134 135 135 /// Add a new equivalence class for each type variable … … 207 207 208 208 /// Private lookup API; returns array index of string, or env.size() for not found 209 ClassList::iterator internal_lookup( const std::string& );209 ClassList::iterator internal_lookup( const TypeInstType::TypeEnvKey & ); 210 210 }; 211 211 -
src/AST/TypeSubstitution.cpp
r42f6e07 r2b4daf2 39 39 void TypeSubstitution::initialize( const TypeSubstitution &src, TypeSubstitution &dest ) { 40 40 dest.typeEnv.clear(); 41 dest.varEnv.clear();42 41 dest.add( src ); 43 42 } … … 47 46 typeEnv[ i->first ] = i->second; 48 47 } // for 49 for ( VarEnvType::const_iterator i = other.varEnv.begin(); i != other.varEnv.end(); ++i ) {50 varEnv[ i->first ] = i->second;51 } // for52 48 } 53 49 54 void TypeSubstitution::add( std::stringformalType, const Type *actualType ) {55 typeEnv[ formalType ] = actualType;50 void TypeSubstitution::add( const TypeInstType * formalType, const Type *actualType ) { 51 typeEnv[ *formalType ] = actualType; 56 52 } 57 53 58 void TypeSubstitution::add Var( std::string formalExpr, const Expr *actualExpr) {59 varEnv[ formalExpr ] = actualExpr;54 void TypeSubstitution::add( const TypeInstType::TypeEnvKey & key, const Type * actualType) { 55 typeEnv[ key ] = actualType; 60 56 } 61 57 62 void TypeSubstitution::remove( std::stringformalType ) {63 TypeEnvType::iterator i = typeEnv.find( formalType );58 void TypeSubstitution::remove( const TypeInstType * formalType ) { 59 TypeEnvType::iterator i = typeEnv.find( *formalType ); 64 60 if ( i != typeEnv.end() ) { 65 typeEnv.erase( formalType );61 typeEnv.erase( *formalType ); 66 62 } // if 67 63 } 68 64 69 const Type *TypeSubstitution::lookup( std::stringformalType ) const {70 TypeEnvType::const_iterator i = typeEnv.find( formalType );65 const Type *TypeSubstitution::lookup( const TypeInstType * formalType ) const { 66 TypeEnvType::const_iterator i = typeEnv.find( *formalType ); 71 67 72 68 // break on not in substitution set … … 75 71 // attempt to transitively follow TypeInstType links. 76 72 while ( const TypeInstType *actualType = i->second.as<TypeInstType>()) { 77 const std::string& typeName = actualType->name;78 79 73 // break cycles in the transitive follow 80 if ( formalType == typeName ) break;74 if ( *formalType == *actualType ) break; 81 75 82 76 // Look for the type this maps to, returning previous mapping if none-such 83 i = typeEnv.find( typeName );77 i = typeEnv.find( *actualType ); 84 78 if ( i == typeEnv.end() ) return actualType; 85 79 } … … 90 84 91 85 bool TypeSubstitution::empty() const { 92 return typeEnv.empty() && varEnv.empty();86 return typeEnv.empty(); 93 87 } 94 88 … … 98 92 TypeSubstitution * newEnv; 99 93 EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){} 100 void previsit( TypeDecl * tyDecl) {94 void previsit( FunctionType * ftype ) { 101 95 // transfer known bindings for seen type variables 102 if ( const Type * t = env->lookup( tyDecl->name ) ) { 103 newEnv->add( tyDecl->name, t ); 96 for (auto & formal : ftype->forall) { 97 if ( const Type * t = env->lookup( formal ) ) { 98 newEnv->add( formal, t ); 99 } 104 100 } 105 101 } … … 130 126 131 127 const Type * TypeSubstitution::Substituter::postvisit( const TypeInstType *inst ) { 132 BoundVarsType::const_iterator bound = boundVars.find( inst->name);128 BoundVarsType::const_iterator bound = boundVars.find( *inst ); 133 129 if ( bound != boundVars.end() ) return inst; 134 130 135 TypeEnvType::const_iterator i = sub.typeEnv.find( inst->name);131 TypeEnvType::const_iterator i = sub.typeEnv.find( *inst ); 136 132 if ( i == sub.typeEnv.end() ) { 137 133 return inst; … … 141 137 // TODO: investigate preventing type variables from being bound to themselves in the first place. 142 138 if ( const TypeInstType * replacement = i->second.as<TypeInstType>() ) { 143 if ( inst->name == replacement->name) {139 if ( *inst == *replacement ) { 144 140 return inst; 145 141 } … … 156 152 } 157 153 158 const Expr * TypeSubstitution::Substituter::postvisit( const NameExpr * nameExpr ) {159 VarEnvType::const_iterator i = sub.varEnv.find( nameExpr->name );160 if ( i == sub.varEnv.end() ) {161 return nameExpr;162 } else {163 subCount++;164 return i->second;165 } // if166 }167 168 154 void TypeSubstitution::Substituter::previsit( const FunctionType * ptype ) { 169 155 GuardValue( boundVars ); 170 156 // bind type variables from forall-qualifiers 171 157 if ( freeOnly ) { 172 for ( const TypeDecl *tyvar : ptype->forall ) {173 boundVars.insert( tyvar->name);158 for ( auto & tyvar : ptype->forall ) { 159 boundVars.insert( *tyvar ); 174 160 } // for 175 161 } // if 176 162 } 177 163 164 /* 178 165 void TypeSubstitution::Substituter::handleAggregateType( const BaseInstType * type ) { 179 166 GuardValue( boundVars ); … … 184 171 if ( ! type->params.empty() ) { 185 172 for ( const TypeDecl * tyvar : decl->params ) { 186 boundVars.insert( tyvar->name);173 boundVars.insert( *tyvar ); 187 174 } // for 188 175 } // if … … 198 185 handleAggregateType( aggregateUseType ); 199 186 } 187 */ 200 188 201 189 } // namespace ast -
src/AST/TypeSubstitution.hpp
r42f6e07 r2b4daf2 69 69 } 70 70 71 void add( std::string formalType, const Type *actualType ); 71 void add( const TypeInstType * formalType, const Type *actualType ); 72 void add( const TypeInstType::TypeEnvKey & key, const Type *actualType ); 72 73 void add( const TypeSubstitution &other ); 73 void remove( std::stringformalType );74 const Type *lookup( std::stringformalType ) const;74 void remove( const TypeInstType * formalType ); 75 const Type *lookup( const TypeInstType * formalType ) const; 75 76 bool empty() const; 76 77 void addVar( std::string formalExpr, const Expr *actualExpr );78 77 79 78 template< typename FormalIterator, typename ActualIterator > … … 101 100 friend class Pass; 102 101 103 typedef std::unordered_map< std::string, ptr<Type> > TypeEnvType; 104 typedef std::unordered_map< std::string, ptr<Expr> > VarEnvType; 102 typedef std::unordered_map< TypeInstType::TypeEnvKey, ptr<Type> > TypeEnvType; 105 103 TypeEnvType typeEnv; 106 VarEnvType varEnv;107 104 108 105 public: … … 113 110 auto end() const -> decltype( typeEnv. end() ) { return typeEnv. end(); } 114 111 115 auto beginVar() -> decltype( varEnv.begin() ) { return varEnv.begin(); }116 auto endVar() -> decltype( varEnv. end() ) { return varEnv. end(); }117 auto beginVar() const -> decltype( varEnv.begin() ) { return varEnv.begin(); }118 auto endVar() const -> decltype( varEnv. end() ) { return varEnv. end(); }119 112 }; 120 113 114 // this is the only place where type parameters outside a function formal may be substituted. 121 115 template< typename FormalIterator, typename ActualIterator > 122 116 void TypeSubstitution::add( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) { … … 129 123 if ( const TypeExpr *actual = actualIt->template as<TypeExpr>() ) { 130 124 if ( formal->name != "" ) { 131 typeEnv[ formal ->name] = actual->type;125 typeEnv[ formal ] = actual->type; 132 126 } // if 133 127 } else { … … 135 129 } // if 136 130 } else { 137 // TODO: type check the formal and actual parameters 138 if ( (*formalIt)->name != "" ) { 139 varEnv[ (*formalIt)->name ] = *actualIt; 140 } // if 131 141 132 } // if 142 133 } // for 143 134 } 135 136 144 137 145 138 template< typename FormalIterator, typename ActualIterator > … … 147 140 add( formalBegin, formalEnd, actualBegin ); 148 141 } 142 149 143 150 144 } // namespace ast … … 164 158 165 159 const Type * postvisit( const TypeInstType * aggregateUseType ); 166 const Expr * postvisit( const NameExpr * nameExpr );167 160 168 161 /// Records type variable bindings from forall-statements 169 162 void previsit( const FunctionType * type ); 170 163 /// Records type variable bindings from forall-statements and instantiations of generic types 171 void handleAggregateType( const BaseInstType * type );164 // void handleAggregateType( const BaseInstType * type ); 172 165 173 void previsit( const StructInstType * aggregateUseType );174 void previsit( const UnionInstType * aggregateUseType );166 // void previsit( const StructInstType * aggregateUseType ); 167 // void previsit( const UnionInstType * aggregateUseType ); 175 168 176 169 const TypeSubstitution & sub; 177 170 int subCount = 0; 178 171 bool freeOnly; 179 typedef std::unordered_set< std::string> BoundVarsType;172 typedef std::unordered_set< TypeInstType::TypeEnvKey > BoundVarsType; 180 173 BoundVarsType boundVars; 181 174 -
src/AST/module.mk
r42f6e07 r2b4daf2 33 33 AST/Expr.cpp \ 34 34 AST/Expr.hpp \ 35 AST/ForallSubstitutionTable.cpp \36 AST/ForallSubstitutionTable.hpp \37 AST/ForallSubstitutor.hpp \38 35 AST/FunctionSpec.hpp \ 39 36 AST/Fwd.hpp \ -
src/GenPoly/GenPoly.cc
r42f6e07 r2b4daf2 115 115 if (!env) return type; 116 116 if (auto typeInst = dynamic_cast<const ast::TypeInstType*> (type)) { 117 auto newType = env->lookup(typeInst ->name);117 auto newType = env->lookup(typeInst); 118 118 if (newType) return newType; 119 119 } … … 172 172 173 173 if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( type ) ) { 174 return tyVars.find(typeInst-> name) != tyVars.end() ? type : nullptr;174 return tyVars.find(typeInst->typeString()) != tyVars.end() ? type : nullptr; 175 175 } else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) { 176 176 return isPolyType( arrayType->base, env ); … … 552 552 } 553 553 554 void addToTyVarMap( const ast::Type Decl* tyVar, TyVarMap & tyVarMap) {555 tyVarMap.insert(tyVar-> name, convData(ast::TypeDecl::Data{tyVar}));554 void addToTyVarMap( const ast::TypeInstType * tyVar, TyVarMap & tyVarMap) { 555 tyVarMap.insert(tyVar->typeString(), convData(ast::TypeDecl::Data{tyVar->base})); 556 556 } 557 557 -
src/InitTweak/GenInit.cc
r42f6e07 r2b4daf2 122 122 }; 123 123 124 struct HoistArrayDimension_NoResolve final : public WithDeclsToAdd, public WithShortCircuiting, public WithGuards { 125 /// hoist dimension from array types in object declaration so that it uses a single 126 /// const variable of type size_t, so that side effecting array dimensions are only 127 /// computed once. 128 static void hoistArrayDimension( std::list< Declaration * > & translationUnit ); 129 130 void premutate( ObjectDecl * objectDecl ); 131 DeclarationWithType * postmutate( ObjectDecl * objectDecl ); 132 void premutate( FunctionDecl *functionDecl ); 133 // should not traverse into any of these declarations to find objects 134 // that need to be constructed or destructed 135 void premutate( AggregateDecl * ) { visit_children = false; } 136 void premutate( NamedTypeDecl * ) { visit_children = false; } 137 void premutate( FunctionType * ) { visit_children = false; } 138 139 void hoist( Type * type ); 140 141 Type::StorageClasses storageClasses; 142 bool inFunction = false; 143 }; 144 124 145 void genInit( std::list< Declaration * > & translationUnit ) { 125 HoistArrayDimension::hoistArrayDimension( translationUnit ); 146 if (!useNewAST) { 147 HoistArrayDimension::hoistArrayDimension( translationUnit ); 148 } 149 else { 150 HoistArrayDimension_NoResolve::hoistArrayDimension( translationUnit ); 151 } 126 152 fixReturnStatements( translationUnit ); 127 153 … … 196 222 197 223 // need to resolve array dimensions in order to accurately determine if constexpr 198 if (!useNewAST) { 199 ResolvExpr::findSingleExpression( arrayType->dimension, Validate::SizeType->clone(), indexer ); 200 // array is variable-length when the dimension is not constexpr 201 arrayType->isVarLen = ! isConstExpr( arrayType->dimension ); 202 } 224 ResolvExpr::findSingleExpression( arrayType->dimension, Validate::SizeType->clone(), indexer ); 225 // array is variable-length when the dimension is not constexpr 226 arrayType->isVarLen = ! isConstExpr( arrayType->dimension ); 203 227 // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects. 204 228 // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve … … 218 242 219 243 void HoistArrayDimension::premutate( FunctionDecl * ) { 244 GuardValue( inFunction ); 245 inFunction = true; 246 } 247 248 // precompute array dimension expression, because constructor generation may duplicate it, 249 // which would be incorrect if it is a side-effecting computation. 250 void HoistArrayDimension_NoResolve::hoistArrayDimension( std::list< Declaration * > & translationUnit ) { 251 PassVisitor<HoistArrayDimension_NoResolve> hoister; 252 mutateAll( translationUnit, hoister ); 253 } 254 255 void HoistArrayDimension_NoResolve::premutate( ObjectDecl * objectDecl ) { 256 GuardValue( storageClasses ); 257 storageClasses = objectDecl->get_storageClasses(); 258 } 259 260 DeclarationWithType * HoistArrayDimension_NoResolve::postmutate( ObjectDecl * objectDecl ) { 261 hoist( objectDecl->get_type() ); 262 return objectDecl; 263 } 264 265 void HoistArrayDimension_NoResolve::hoist( Type * type ) { 266 // if in function, generate const size_t var 267 static UniqueName dimensionName( "_array_dim" ); 268 269 // C doesn't allow variable sized arrays at global scope or for static variables, so don't hoist dimension. 270 if ( ! inFunction ) return; 271 if ( storageClasses.is_static ) return; 272 273 if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) { 274 if ( ! arrayType->get_dimension() ) return; // xxx - recursive call to hoist? 275 // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects. 276 // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve 277 // still try to detect constant expressions 278 if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return; 279 280 ObjectDecl * arrayDimension = new ObjectDecl( dimensionName.newName(), storageClasses, LinkageSpec::C, 0, Validate::SizeType->clone(), new SingleInit( arrayType->get_dimension() ) ); 281 arrayDimension->get_type()->set_const( true ); 282 283 arrayType->set_dimension( new VariableExpr( arrayDimension ) ); 284 declsToAddBefore.push_back( arrayDimension ); 285 286 hoist( arrayType->get_base() ); 287 return; 288 } 289 } 290 291 void HoistArrayDimension_NoResolve::premutate( FunctionDecl * ) { 220 292 GuardValue( inFunction ); 221 293 inFunction = true; -
src/ResolvExpr/AdjustExprType.cc
r42f6e07 r2b4daf2 133 133 const ast::Type * postvisit( const ast::TypeInstType * inst ) { 134 134 // replace known function-type-variables with pointer-to-function 135 if ( const ast::EqvClass * eqvClass = tenv.lookup( inst->name) ) {135 if ( const ast::EqvClass * eqvClass = tenv.lookup( *inst ) ) { 136 136 if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) { 137 137 return new ast::PointerType{ inst }; -
src/ResolvExpr/CandidateFinder.cpp
r42f6e07 r2b4daf2 212 212 // mark type variable and specialization cost of forall clause 213 213 convCost.incVar( function->forall.size() ); 214 for ( const ast::TypeDecl * td : function->forall ) { 215 convCost.decSpec( td->assertions.size() ); 216 } 214 convCost.decSpec( function->assertions.size() ); 217 215 218 216 return convCost; … … 223 221 ast::AssertionSet & need 224 222 ) { 225 for ( const ast::TypeDecl *tyvar : type->forall ) {226 unifiableVars[ tyvar->name ] = ast::TypeDecl::Data{ tyvar};227 for ( const ast::DeclWithType * assn : tyvar->assertions ) {228 need[ assn ].isUsed = true;229 }223 for ( auto & tyvar : type->forall ) { 224 unifiableVars[ *tyvar ] = ast::TypeDecl::Data{ tyvar->base }; 225 } 226 for ( auto & assn : type->assertions ) { 227 need[ assn ].isUsed = true; 230 228 } 231 229 } … … 907 905 // xxx - is it possible that handleTupleAssignment and main finder both produce candidates? 908 906 // this means there exists ctor/assign functions with a tuple as first parameter. 909 funcFinder.find( untypedExpr->func, selfFinder.candidates.empty() ? ResolvMode::withAdjustment() : ResolvMode::withoutFailFast() ); 907 ResolvMode mode = { 908 true, // adjust 909 !untypedExpr->func.as<ast::NameExpr>(), // prune if not calling by name 910 selfFinder.candidates.empty() // failfast if other options are not found 911 }; 912 funcFinder.find( untypedExpr->func, mode ); 910 913 // short-circuit if no candidates 911 914 // if ( funcFinder.candidates.empty() ) return; … … 953 956 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult ) 954 957 ) { 955 if ( const ast::EqvClass * clz = func->env.lookup( inst->name) ) {958 if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) { 956 959 if ( auto function = clz->bound.as< ast::FunctionType >() ) { 957 960 CandidateRef newFunc{ new Candidate{ *func } }; … … 1077 1080 assert( toType ); 1078 1081 toType = resolveTypeof( toType, symtab ); 1079 toType = SymTab::validateType( castExpr->location, toType, symtab );1082 // toType = SymTab::validateType( castExpr->location, toType, symtab ); 1080 1083 toType = adjustExprType( toType, tenv, symtab ); 1081 1084 … … 1162 1165 1163 1166 if(auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) { 1164 auto td = cand->env.lookup( insttype->name);1167 auto td = cand->env.lookup(*insttype); 1165 1168 if(!td) { continue; } 1166 1169 expr = td->bound.get(); … … 1568 1571 // calculate target type 1569 1572 const ast::Type * toType = resolveTypeof( initAlt.type, symtab ); 1570 toType = SymTab::validateType( initExpr->location, toType, symtab );1573 // toType = SymTab::validateType( initExpr->location, toType, symtab ); 1571 1574 toType = adjustExprType( toType, tenv, symtab ); 1572 1575 // The call to find must occur inside this loop, otherwise polymorphic return -
src/ResolvExpr/CastCost.cc
r42f6e07 r2b4daf2 202 202 ) { 203 203 if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 204 if ( const ast::EqvClass * eqvClass = env.lookup( typeInst->name) ) {204 if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst ) ) { 205 205 // check cast cost against bound type, if present 206 206 if ( eqvClass->bound ) { -
src/ResolvExpr/CommonType.cc
r42f6e07 r2b4daf2 713 713 const ast::Type * base = oPtr->base; 714 714 if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) { 715 auto entry = open.find( var->name);715 auto entry = open.find( *var ); 716 716 if ( entry != open.end() ) { 717 717 ast::AssertionSet need, have; -
src/ResolvExpr/ConversionCost.cc
r42f6e07 r2b4daf2 498 498 ) { 499 499 if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 500 if ( const ast::EqvClass * eqv = env.lookup( inst->name) ) {500 if ( const ast::EqvClass * eqv = env.lookup( *inst ) ) { 501 501 if ( eqv->bound ) { 502 502 return conversionCost(src, eqv->bound, srcIsLvalue, symtab, env ); … … 675 675 676 676 void ConversionCost_new::postvisit( const ast::TypeInstType * typeInstType ) { 677 if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) {677 if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) { 678 678 cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env ); 679 679 } else if ( const ast::TypeInstType * dstAsInst = 680 680 dynamic_cast< const ast::TypeInstType * >( dst ) ) { 681 if ( typeInstType->name == dstAsInst->name) {681 if ( *typeInstType == *dstAsInst ) { 682 682 cost = Cost::zero; 683 683 } -
src/ResolvExpr/FindOpenVars.cc
r42f6e07 r2b4daf2 112 112 // mark open/closed variables 113 113 if ( nextIsOpen ) { 114 for ( const ast::TypeDecl *decl : type->forall ) {115 open[ decl->name ] = ast::TypeDecl::Data{ decl};116 for ( const ast::DeclWithType * assert : decl->assertions ) {117 need[ assert ].isUsed = false;118 }114 for ( auto & decl : type->forall ) { 115 open[ *decl ] = ast::TypeDecl::Data{ decl->base }; 116 } 117 for ( auto & assert : type->assertions ) { 118 need[ assert ].isUsed = false; 119 119 } 120 120 } else { 121 for ( const ast::TypeDecl *decl : type->forall ) {122 closed[ decl->name ] = ast::TypeDecl::Data{ decl };123 for ( const ast::DeclWithType * assert : decl->assertions ) {124 have[ assert ].isUsed = false;125 }121 for ( auto & decl : type->forall ) { 122 closed[ *decl ] = ast::TypeDecl::Data{ decl->base }; 123 } 124 for ( auto & assert : type->assertions ) { 125 have[ assert ].isUsed = false; 126 126 } 127 127 } -
src/ResolvExpr/PolyCost.cc
r42f6e07 r2b4daf2 68 68 69 69 void previsit( const ast::TypeInstType * type ) { 70 if ( const ast::EqvClass * eqv = env_.lookup( type->name ) ) /* && */ if ( eqv->bound ) {70 if ( const ast::EqvClass * eqv = env_.lookup( *type ) ) /* && */ if ( eqv->bound ) { 71 71 if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) { 72 72 if ( symtab.lookupType( otherType->name ) ) { -
src/ResolvExpr/PtrsAssignable.cc
r42f6e07 r2b4daf2 134 134 } 135 135 void postvisit( const ast::TypeInstType * inst ) { 136 if ( const ast::EqvClass * eqv = typeEnv.lookup( inst->name) ) {136 if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst ) ) { 137 137 if ( eqv->bound ) { 138 138 // T * = S * for any S depends on the type bound to T … … 146 146 const ast::TypeEnvironment & env ) { 147 147 if ( const ast::TypeInstType * dstAsInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 148 if ( const ast::EqvClass * eqv = env.lookup( dstAsInst->name) ) {148 if ( const ast::EqvClass * eqv = env.lookup( *dstAsInst ) ) { 149 149 return ptrsAssignable( src, eqv->bound, env ); 150 150 } -
src/ResolvExpr/PtrsCastable.cc
r42f6e07 r2b4daf2 180 180 } 181 181 } 182 } else if ( const ast::EqvClass * eqvClass = env.lookup( inst->name) ) {182 } else if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) { 183 183 if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) { 184 184 return -1; … … 283 283 ) { 284 284 if ( auto inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 285 if ( const ast::EqvClass * eqvClass = env.lookup( inst->name) ) {285 if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) { 286 286 return ptrsAssignable( src, eqvClass->bound, env ); 287 287 } -
src/ResolvExpr/RenameVars.cc
r42f6e07 r2b4daf2 19 19 #include <utility> // for pair 20 20 21 #include "AST/ForallSubstitutionTable.hpp"22 21 #include "AST/Pass.hpp" 23 22 #include "AST/Type.hpp" … … 39 38 int level = 0; 40 39 int resetCount = 0; 40 41 int next_expr_id = 1; 42 int next_usage_id = 1; 41 43 ScopedMap< std::string, std::string > nameMap; 44 ScopedMap< std::string, ast::TypeInstType::TypeEnvKey > idMap; 42 45 public: 43 ast::ForallSubstitutionTable subs;44 45 46 void reset() { 46 47 level = 0; … … 53 54 type->name = it->second; 54 55 } 56 } 57 58 void nextUsage() { 59 ++next_usage_id; 55 60 } 56 61 … … 78 83 79 84 const ast::TypeInstType * rename( const ast::TypeInstType * type ) { 80 // re-linking of base type handled by WithForallSubstitutor81 82 85 // rename 83 auto it = nameMap.find( type->name ); 84 if ( it != nameMap.end() ) { 85 // unconditionally mutate because map will *always* have different name, 86 // if this mutates, will *always* have been mutated by ForallSubstitutor above 87 ast::TypeInstType * mut = ast::mutate( type ); 88 mut->name = it->second; 86 auto it = idMap.find( type->name ); 87 if ( it != idMap.end() ) { 88 // unconditionally mutate because map will *always* have different name 89 ast::TypeInstType * mut = ast::shallowCopy( type ); 90 // reconcile base node since some copies might have been made 91 mut->base = it->second.base; 92 mut->formal_usage = it->second.formal_usage; 93 mut->expr_id = it->second.expr_id; 89 94 type = mut; 90 95 } … … 93 98 } 94 99 95 const ast::FunctionType * openLevel( const ast::FunctionType * type ) {100 const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) { 96 101 if ( type->forall.empty() ) return type; 97 98 nameMap.beginScope(); 102 idMap.beginScope(); 99 103 100 104 // Load new names from this forall clause and perform renaming. 101 auto mutType = ast::mutate( type ); 102 assert( type == mutType && "mutated type must be unique from ForallSubstitutor" ); 103 for ( ast::ptr< ast::TypeDecl > & td : mutType->forall ) { 104 assertf(dynamic_cast<ast::FunctionType *>(mutType), "renaming vars in non-function type"); 105 std::ostringstream output; 106 output << "_" << resetCount << "_" << level << "_" << td->name; 107 std::string newname = output.str(); 108 nameMap[ td->name ] = newname; 109 ++level; 110 111 ast::TypeDecl * mutDecl = ast::mutate( td.get() ); 112 assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" ); 113 mutDecl->name = newname; 114 // assertion above means `td = mutDecl;` is unnecessary 115 } 116 // assertion above means `type = mutType;` is unnecessary 117 118 return type; 105 auto mutType = ast::shallowCopy( type ); 106 // assert( type == mutType && "mutated type must be unique from ForallSubstitutor" ); 107 for ( auto & td : mutType->forall ) { 108 auto mut = ast::shallowCopy( td.get() ); 109 // assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" ); 110 111 if (mode == GEN_EXPR_ID) { 112 mut->expr_id = next_expr_id; 113 mut->formal_usage = -1; 114 ++next_expr_id; 115 } 116 else if (mode == GEN_USAGE) { 117 assertf(mut->expr_id, "unfilled expression id in generating candidate type"); 118 mut->formal_usage = next_usage_id; 119 } 120 else { 121 assert(false); 122 } 123 idMap[ td->name ] = ast::TypeInstType::TypeEnvKey(*mut); 124 125 td = mut; 126 } 127 128 return mutType; 119 129 } 120 130 121 131 void closeLevel( const ast::FunctionType * type ) { 122 132 if ( type->forall.empty() ) return; 123 124 nameMap.endScope(); 133 idMap.endScope(); 125 134 } 126 135 }; … … 142 151 }; 143 152 144 struct RenameVars_new /*: public ast::WithForallSubstitutor*/ { 145 #warning when old RenameVars goes away, replace hack below with global pass inheriting from WithForallSubstitutor 146 ast::ForallSubstitutionTable & subs = renaming.subs; 153 struct RenameVars_new : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ { 154 RenameMode mode; 147 155 148 156 const ast::FunctionType * previsit( const ast::FunctionType * type ) { 149 return renaming.openLevel( type );157 return renaming.openLevel( type, mode ); 150 158 } 151 159 … … 163 171 164 172 const ast::TypeInstType * previsit( const ast::TypeInstType * type ) { 173 if (mode == GEN_USAGE && !type->formal_usage) return type; // do not rename an actual type 165 174 return renaming.rename( type ); 166 175 } … … 177 186 } 178 187 179 const ast::Type * renameTyVars( const ast::Type * t ) {180 ast::Type *tc = ast::deepCopy(t);188 const ast::Type * renameTyVars( const ast::Type * t, RenameMode mode, bool reset ) { 189 // ast::Type *tc = ast::deepCopy(t); 181 190 ast::Pass<RenameVars_new> renamer; 182 // return t->accept( renamer ); 183 return tc->accept( renamer ); 191 renamer.core.mode = mode; 192 if (mode == GEN_USAGE && reset) { 193 renaming.nextUsage(); 194 } 195 return t->accept( renamer ); 184 196 } 185 197 186 198 void resetTyVarRenaming() { 187 199 renaming.reset(); 200 renaming.nextUsage(); 188 201 } 189 202 -
src/ResolvExpr/RenameVars.h
r42f6e07 r2b4daf2 30 30 /// Provides a consistent renaming of forall type names in a hierarchy by prefixing them with a unique "level" ID 31 31 void renameTyVars( Type * ); 32 const ast::Type * renameTyVars( const ast::Type * ); 32 33 enum RenameMode { 34 GEN_USAGE, // for type in VariableExpr 35 GEN_EXPR_ID // for type in decl 36 }; 37 const ast::Type * renameTyVars( const ast::Type *, RenameMode mode = GEN_USAGE, bool reset = true ); 38 33 39 34 40 /// resets internal state of renamer to avoid overflow 35 41 void resetTyVarRenaming(); 42 43 36 44 } // namespace ResolvExpr 37 45 -
src/ResolvExpr/ResolvMode.h
r42f6e07 r2b4daf2 23 23 const bool failFast; ///< Fail on no resulting alternatives? [true] 24 24 25 private:26 25 constexpr ResolvMode(bool a, bool p, bool ff) 27 26 : adjust(a), prune(p), failFast(ff) {} 28 27 29 public:30 28 /// Default settings 31 29 constexpr ResolvMode() : adjust(false), prune(true), failFast(true) {} -
src/ResolvExpr/ResolveAssertions.cc
r42f6e07 r2b4daf2 397 397 398 398 /// Limit to depth of recursion of assertion satisfaction 399 static const int recursionLimit = 4;399 static const int recursionLimit = 7; 400 400 /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of 401 401 static const int deferLimit = 10; -
src/ResolvExpr/ResolveTypeof.cc
r42f6e07 r2b4daf2 15 15 16 16 #include "ResolveTypeof.h" 17 #include "RenameVars.h" 17 18 18 19 #include <cassert> // for assert … … 218 219 mutDecl->mangleName = Mangle::mangle(mutDecl); // do not mangle unnamed variables 219 220 221 mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID); 220 222 mutDecl->isTypeFixed = true; 221 223 return mutDecl; -
src/ResolvExpr/Resolver.cc
r42f6e07 r2b4daf2 986 986 }; 987 987 } // anonymous namespace 988 989 988 /// Check if this expression is or includes a deleted expression 990 989 const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) { … … 1152 1151 const ast::Expr * untyped, const ast::SymbolTable & symtab 1153 1152 ) { 1154 resetTyVarRenaming();1155 1153 ast::TypeEnvironment env; 1156 1154 ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, symtab, env ); … … 1312 1310 } 1313 1311 1314 ast::ptr< ast::Expr >resolveStmtExpr(1312 const ast::Expr * resolveStmtExpr( 1315 1313 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab 1316 1314 ) { 1317 1315 assert( stmtExpr ); 1318 1316 ast::Pass< Resolver_new > resolver{ symtab }; 1319 ast::ptr< ast::Expr > ret = stmtExpr; 1320 ret = ret->accept( resolver ); 1321 strict_dynamic_cast< ast::StmtExpr * >( ret.get_and_mutate() )->computeResult(); 1317 auto ret = mutate(stmtExpr->accept(resolver)); 1318 strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult(); 1322 1319 return ret; 1323 1320 } … … 1375 1372 } 1376 1373 1377 // handle assertions . (seems deep)1374 // handle assertions 1378 1375 1379 1376 symtab.enterScope(); 1380 for (auto & typeParam : mutType->forall) { 1381 auto mutParam = typeParam.get_and_mutate(); 1382 symtab.addType(mutParam); 1383 for (auto & asst : mutParam->assertions) { 1384 asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab); 1385 symtab.addId(asst); 1386 } 1387 typeParam = mutParam; 1377 mutType->forall.clear(); 1378 mutType->assertions.clear(); 1379 for (auto & typeParam : mutDecl->type_params) { 1380 symtab.addType(typeParam); 1381 mutType->forall.emplace_back(new ast::TypeInstType(typeParam->name, typeParam)); 1382 } 1383 for (auto & asst : mutDecl->assertions) { 1384 asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab); 1385 symtab.addId(asst); 1386 mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst)); 1388 1387 } 1389 1388 … … 1407 1406 mutType->returns = std::move(returnTypes); 1408 1407 1408 auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID)); 1409 1409 1410 std::list<ast::ptr<ast::Stmt>> newStmts; 1410 1411 resolveWithExprs (mutDecl->withExprs, newStmts); … … 1418 1419 symtab.leaveScope(); 1419 1420 1421 mutDecl->type = renamedType; 1420 1422 mutDecl->mangleName = Mangle::mangle(mutDecl); 1421 1423 mutDecl->isTypeFixed = true; … … 1534 1536 const PtrType * handlePtrType( const PtrType * type, const ast::SymbolTable & symtab ) { 1535 1537 if ( type->dimension ) { 1536 #warning should use new equivalent to Validate::SizeType rather than sizeType here 1537 ast::ptr< ast::Type > sizeType = new ast::BasicType{ ast::BasicType::LongUnsignedInt }; 1538 ast::ptr< ast::Type > sizeType = ast::sizeType; 1538 1539 ast::mutate_field( 1539 1540 type, &PtrType::dimension, -
src/ResolvExpr/Resolver.h
r42f6e07 r2b4daf2 72 72 ast::ptr< ast::Init > resolveCtorInit( 73 73 const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab ); 74 /// Resolves a statement expression 75 ast::ptr< ast::Expr >resolveStmtExpr(74 /// Resolves a statement expression 75 const ast::Expr * resolveStmtExpr( 76 76 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab ); 77 77 } // namespace ResolvExpr -
src/ResolvExpr/SatisfyAssertions.cpp
r42f6e07 r2b4daf2 57 57 ast::UniqueId resnSlot; ///< Slot for any recursive assertion IDs 58 58 59 AssnCandidate( 60 const ast::SymbolTable::IdData c, const ast::Type * at, ast::TypeEnvironment && e, 59 AssnCandidate( 60 const ast::SymbolTable::IdData c, const ast::Type * at, ast::TypeEnvironment && e, 61 61 ast::AssertionSet && h, ast::AssertionSet && n, ast::OpenVarSet && o, ast::UniqueId rs ) 62 : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ), 62 : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ), 63 63 need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {} 64 64 }; … … 69 69 /// Reference to a single deferred item 70 70 struct DeferRef { 71 const ast:: DeclWithType * decl;71 const ast::VariableExpr * expr; 72 72 const ast::AssertionSetValue & info; 73 73 const AssnCandidate & match; 74 74 }; 75 76 /// Wrapper for the deferred items from a single assertion satisfaction. 75 76 /// Wrapper for the deferred items from a single assertion satisfaction. 77 77 /// Acts like an indexed list of DeferRef 78 78 struct DeferItem { 79 const ast:: DeclWithType * decl;79 const ast::VariableExpr * expr; 80 80 const ast::AssertionSetValue & info; 81 81 AssnCandidateList matches; 82 82 83 DeferItem( 84 const ast:: DeclWithType* d, const ast::AssertionSetValue & i, AssnCandidateList && ms )85 : decl( d ), info( i ), matches( std::move( ms ) ) {}83 DeferItem( 84 const ast::VariableExpr * d, const ast::AssertionSetValue & i, AssnCandidateList && ms ) 85 : expr( d ), info( i ), matches( std::move( ms ) ) {} 86 86 87 87 bool empty() const { return matches.empty(); } … … 89 89 AssnCandidateList::size_type size() const { return matches.size(); } 90 90 91 DeferRef operator[] ( unsigned i ) const { return { decl, info, matches[i] }; }91 DeferRef operator[] ( unsigned i ) const { return { expr, info, matches[i] }; } 92 92 }; 93 93 … … 117 117 /// Initial satisfaction state for a candidate 118 118 SatState( CandidateRef & c, const ast::SymbolTable & syms ) 119 : cand( c ), need(), newNeed(), deferred(), inferred(), costs{ Cost::zero }, 119 : cand( c ), need(), newNeed(), deferred(), inferred(), costs{ Cost::zero }, 120 120 symtab( syms ) { need.swap( c->need ); } 121 121 122 122 /// Update satisfaction state for next step after previous state 123 123 SatState( SatState && o, IterateFlag ) 124 : cand( std::move( o.cand ) ), need( o.newNeed.begin(), o.newNeed.end() ), newNeed(), 125 deferred(), inferred( std::move( o.inferred ) ), costs( std::move( o.costs ) ), 124 : cand( std::move( o.cand ) ), need( o.newNeed.begin(), o.newNeed.end() ), newNeed(), 125 deferred(), inferred( std::move( o.inferred ) ), costs( std::move( o.costs ) ), 126 126 symtab( o.symtab ) { costs.emplace_back( Cost::zero ); } 127 127 128 128 /// Field-wise next step constructor 129 129 SatState( 130 CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs, 130 CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs, 131 131 ast::SymbolTable && syms ) 132 : cand( std::move( c ) ), need( nn.begin(), nn.end() ), newNeed(), deferred(), 132 : cand( std::move( c ) ), need( nn.begin(), nn.end() ), newNeed(), deferred(), 133 133 inferred( std::move( i ) ), costs( std::move( cs ) ), symtab( std::move( syms ) ) 134 134 { costs.emplace_back( Cost::zero ); } … … 138 138 void addToSymbolTable( const ast::AssertionSet & have, ast::SymbolTable & symtab ) { 139 139 for ( auto & i : have ) { 140 if ( i.second.isUsed ) { symtab.addId( i.first ); }140 if ( i.second.isUsed ) { symtab.addId( i.first->var ); } 141 141 } 142 142 } 143 143 144 144 /// Binds a single assertion, updating satisfaction state 145 void bindAssertion( 146 const ast:: DeclWithType * decl, const ast::AssertionSetValue & info, CandidateRef & cand,145 void bindAssertion( 146 const ast::VariableExpr * expr, const ast::AssertionSetValue & info, CandidateRef & cand, 147 147 AssnCandidate & match, InferCache & inferred 148 148 ) { 149 149 const ast::DeclWithType * candidate = match.cdata.id; 150 assertf( candidate->uniqueId, 150 assertf( candidate->uniqueId, 151 151 "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() ); 152 152 153 153 ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cvtCost ); 154 154 varExpr->result = match.adjType; … … 156 156 157 157 // place newly-inferred assertion in proper location in cache 158 inferred[ info.resnSlot ][ decl->uniqueId ] = ast::ParamEntry{159 candidate->uniqueId, candidate, match.adjType, decl->get_type(), varExpr };158 inferred[ info.resnSlot ][ expr->var->uniqueId ] = ast::ParamEntry{ 159 candidate->uniqueId, candidate, match.adjType, expr->result, varExpr }; 160 160 } 161 161 … … 169 169 170 170 std::vector<ast::SymbolTable::IdData> candidates; 171 auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first-> name);171 auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->var->name); 172 172 if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) { 173 173 // prefilter special decls by argument type, if already known 174 ast::ptr<ast::Type> thisArgType = strict_dynamic_cast<const ast::PointerType *>(assn.first->get_type())->base174 ast::ptr<ast::Type> thisArgType = assn.first->result.strict_as<ast::PointerType>()->base 175 175 .strict_as<ast::FunctionType>()->params[0] 176 176 .strict_as<ast::ReferenceType>()->base; … … 184 184 } 185 185 else { 186 candidates = sat.symtab.lookupId(assn.first-> name);186 candidates = sat.symtab.lookupId(assn.first->var->name); 187 187 } 188 188 for ( const ast::SymbolTable::IdData & cdata : candidates ) { … … 200 200 ast::TypeEnvironment newEnv{ sat.cand->env }; 201 201 ast::OpenVarSet newOpen{ sat.cand->open }; 202 ast::ptr< ast::Type > toType = assn.first-> get_type();203 ast::ptr< ast::Type > adjType = 204 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) );202 ast::ptr< ast::Type > toType = assn.first->result; 203 ast::ptr< ast::Type > adjType = 204 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ), GEN_USAGE, false ); 205 205 206 206 // only keep candidates which unify … … 213 213 } 214 214 215 matches.emplace_back( 215 matches.emplace_back( 216 216 cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ), 217 217 std::move( newOpen ), crntResnSlot ); … … 287 287 }; 288 288 289 /// Replace ResnSlots with InferParams and add alternative to output list, if it meets pruning 289 /// Replace ResnSlots with InferParams and add alternative to output list, if it meets pruning 290 290 /// threshold. 291 void finalizeAssertions( 292 CandidateRef & cand, InferCache & inferred, PruneMap & thresholds, CostVec && costs, 293 CandidateList & out 291 void finalizeAssertions( 292 CandidateRef & cand, InferCache & inferred, PruneMap & thresholds, CostVec && costs, 293 CandidateList & out 294 294 ) { 295 295 // prune if cheaper alternative for same key has already been generated … … 308 308 } 309 309 310 /// Combo iterator that combines candidates into an output list, merging their environments. 311 /// Rejects an appended candidate if environments cannot be merged. See `Common/FilterCombos.h` 310 /// Combo iterator that combines candidates into an output list, merging their environments. 311 /// Rejects an appended candidate if environments cannot be merged. See `Common/FilterCombos.h` 312 312 /// for description of "combo iterator". 313 313 class CandidateEnvMerger { … … 337 337 // compute conversion cost from satisfying decl to assertion 338 338 cost += computeConversionCost( 339 assn.match.adjType, assn. decl->get_type(), false, symtab, env );339 assn.match.adjType, assn.expr->result, false, symtab, env ); 340 340 341 341 // mark vars+specialization on function-type assertions … … 350 350 cost.incVar( func->forall.size() ); 351 351 352 for ( const ast::TypeDecl * td : func->forall ) { 353 cost.decSpec( td->assertions.size() ); 354 } 352 cost.decSpec( func->assertions.size() ); 355 353 } 356 354 } … … 387 385 388 386 /// Limit to depth of recursion of assertion satisfaction 389 static const int recursionLimit = 4;387 static const int recursionLimit = 8; 390 388 /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of 391 389 static const int deferLimit = 10; 392 390 } // anonymous namespace 393 391 394 void satisfyAssertions( 395 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 392 void satisfyAssertions( 393 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 396 394 std::vector<std::string> & errors 397 395 ) { … … 419 417 if ( it != thresholds.end() && it->second < sat.costs ) goto nextSat; 420 418 421 // make initial pass at matching assertions 422 for ( auto & assn : sat.need ) { 423 // fail early if any assertion is not satisfiable 424 if ( ! satisfyAssertion( assn, sat ) ) { 419 // should a limit be imposed? worst case here is O(n^2) but very unlikely to happen. 420 for (unsigned resetCount = 0; ; ++resetCount) { 421 ast::AssertionList next; 422 resetTyVarRenaming(); 423 // make initial pass at matching assertions 424 for ( auto & assn : sat.need ) { 425 // fail early if any assertion is not satisfiable 426 if ( ! satisfyAssertion( assn, sat ) ) { 427 next.emplace_back(assn); 428 // goto nextSat; 429 } 430 } 431 // success 432 if (next.empty()) break; 433 // fail if nothing resolves 434 else if (next.size() == sat.need.size()) { 425 435 Indenter tabs{ 3 }; 426 436 std::ostringstream ss; … … 428 438 print( ss, *sat.cand, ++tabs ); 429 439 ss << (tabs-1) << "Could not satisfy assertion:\n"; 430 ast::print( ss, assn.first, tabs );440 ast::print( ss, next[0].first, tabs ); 431 441 432 442 errors.emplace_back( ss.str() ); 433 443 goto nextSat; 434 444 } 445 sat.need = std::move(next); 435 446 } 436 447 … … 438 449 // either add successful match or push back next state 439 450 if ( sat.newNeed.empty() ) { 440 finalizeAssertions( 451 finalizeAssertions( 441 452 sat.cand, sat.inferred, thresholds, std::move( sat.costs ), out ); 442 453 } else { … … 451 462 ss << (tabs-1) << "Too many non-unique satisfying assignments for assertions:\n"; 452 463 for ( const auto & d : sat.deferred ) { 453 ast::print( ss, d. decl, tabs );464 ast::print( ss, d.expr, tabs ); 454 465 } 455 466 … … 460 471 std::vector< CandidateEnvMerger::OutType > compatible = filterCombos( 461 472 sat.deferred, CandidateEnvMerger{ sat.cand->env, sat.cand->open, sat.symtab } ); 462 473 463 474 // fail early if no mutually-compatible assertion satisfaction 464 475 if ( compatible.empty() ) { … … 469 480 ss << (tabs-1) << "No mutually-compatible satisfaction for assertions:\n"; 470 481 for ( const auto& d : sat.deferred ) { 471 ast::print( ss, d. decl, tabs );482 ast::print( ss, d.expr, tabs ); 472 483 } 473 484 … … 483 494 // set up next satisfaction state 484 495 CandidateRef nextCand = std::make_shared<Candidate>( 485 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 496 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 486 497 ast::AssertionSet{} /* need moved into satisfaction state */, 487 498 sat.cand->cost, sat.cand->cvtCost ); … … 489 500 ast::AssertionSet nextNewNeed{ sat.newNeed }; 490 501 InferCache nextInferred{ sat.inferred }; 491 502 492 503 CostVec nextCosts{ sat.costs }; 493 504 nextCosts.back() += compat.cost; 494 505 495 506 ast::SymbolTable nextSymtab{ sat.symtab }; 496 507 … … 501 512 nextNewNeed.insert( match.need.begin(), match.need.end() ); 502 513 503 bindAssertion( r. decl, r.info, nextCand, match, nextInferred );514 bindAssertion( r.expr, r.info, nextCand, match, nextInferred ); 504 515 } 505 516 506 517 // either add successful match or push back next state 507 518 if ( nextNewNeed.empty() ) { 508 finalizeAssertions( 519 finalizeAssertions( 509 520 nextCand, nextInferred, thresholds, std::move( nextCosts ), out ); 510 521 } else { 511 nextSats.emplace_back( 512 std::move( nextCand ), std::move( nextNewNeed ), 513 std::move( nextInferred ), std::move( nextCosts ), 522 nextSats.emplace_back( 523 std::move( nextCand ), std::move( nextNewNeed ), 524 std::move( nextInferred ), std::move( nextCosts ), 514 525 std::move( nextSymtab ) ); 515 526 } … … 523 534 nextSats.clear(); 524 535 } 525 536 526 537 // exceeded recursion limit if reaches here 527 538 if ( out.empty() ) { -
src/ResolvExpr/Unify.cc
r42f6e07 r2b4daf2 773 773 774 774 const ast::Type * postvisit( const ast::TypeInstType * typeInst ) { 775 if ( const ast::EqvClass * clz = tenv.lookup( typeInst->name) ) {775 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) { 776 776 // expand ttype parameter into its actual type 777 777 if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) { … … 811 811 /// Creates a tuple type based on a list of DeclWithType 812 812 template< typename Iter > 813 static ast::ptr< ast::Type >tupleFromTypes( Iter crnt, Iter end ) {813 static const ast::Type * tupleFromTypes( Iter crnt, Iter end ) { 814 814 std::vector< ast::ptr< ast::Type > > types; 815 815 while ( crnt != end ) { … … 821 821 } 822 822 823 return { new ast::TupleType{ std::move(types) }};823 return new ast::TupleType{ std::move(types) }; 824 824 } 825 825 … … 888 888 } 889 889 890 static void markAssertionSet( ast::AssertionSet & assns, const ast:: DeclWithType* assn ) {890 static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) { 891 891 auto i = assns.find( assn ); 892 892 if ( i != assns.end() ) { … … 900 900 const ast::FunctionType * type 901 901 ) { 902 for ( const auto & tyvar : type->forall ) { 903 for ( const ast::DeclWithType * assert : tyvar->assertions ) { 904 markAssertionSet( assn1, assert ); 905 markAssertionSet( assn2, assert ); 906 } 902 for ( auto & assert : type->assertions ) { 903 markAssertionSet( assn1, assert ); 904 markAssertionSet( assn2, assert ); 907 905 } 908 906 } … … 1030 1028 1031 1029 void postvisit( const ast::TypeInstType * typeInst ) { 1032 assert( open.find( typeInst->name) == open.end() );1030 assert( open.find( *typeInst ) == open.end() ); 1033 1031 handleRefType( typeInst, type2 ); 1034 1032 } … … 1036 1034 private: 1037 1035 /// Creates a tuple type based on a list of Type 1038 static ast::ptr< ast::Type >tupleFromTypes(1036 static const ast::Type * tupleFromTypes( 1039 1037 const std::vector< ast::ptr< ast::Type > > & tys 1040 1038 ) { … … 1171 1169 auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 ); 1172 1170 ast::OpenVarSet::const_iterator 1173 entry1 = var1 ? open.find( var1->name) : open.end(),1174 entry2 = var2 ? open.find( var2->name) : open.end();1171 entry1 = var1 ? open.find( *var1 ) : open.end(), 1172 entry2 = var2 ? open.find( *var2 ) : open.end(); 1175 1173 bool isopen1 = entry1 != open.end(); 1176 1174 bool isopen2 = entry2 != open.end(); -
src/SymTab/Mangler.cc
r42f6e07 r2b4daf2 671 671 int dcount = 0, fcount = 0, vcount = 0, acount = 0; 672 672 mangleName += Encoding::forall; 673 for ( const ast::TypeDecl *decl : ptype->forall ) {673 for ( auto & decl : ptype->forall ) { 674 674 switch ( decl->kind ) { 675 675 case ast::TypeDecl::Kind::Dtype: … … 686 686 } // switch 687 687 varNums[ decl->name ] = std::make_pair( nextVarNum, (int)decl->kind ); 688 for ( const ast::DeclWithType * assert : decl->assertions ) {689 ast::Pass<Mangler_new> sub_mangler(690 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums );691 assert->accept( sub_mangler);692 assertionNames.push_back( sub_mangler.core.get_mangleName());693 acount++;694 } // for688 } // for 689 for ( auto & assert : ptype->assertions ) { 690 ast::Pass<Mangler_new> sub_mangler( 691 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums ); 692 assert->var->accept( sub_mangler ); 693 assertionNames.push_back( sub_mangler.core.get_mangleName() ); 694 acount++; 695 695 } // for 696 696 mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_"; -
src/SymTab/Validate.cc
r42f6e07 r2b4daf2 271 271 }; 272 272 273 struct ArrayLength : public WithIndexer{273 struct InitializerLength { 274 274 /// for array types without an explicit length, compute the length and store it so that it 275 275 /// is known to the rest of the phases. For example, … … 282 282 283 283 void previsit( ObjectDecl * objDecl ); 284 }; 285 286 struct ArrayLength : public WithIndexer { 287 static void computeLength( std::list< Declaration * > & translationUnit ); 288 284 289 void previsit( ArrayType * arrayType ); 285 290 }; … … 382 387 FixObjectType::fix, translationUnit); 383 388 } 384 Stats::Time::TimeCall("Array Length", 385 ArrayLength::computeLength, translationUnit); 389 Stats::Time::TimeCall("Initializer Length", 390 InitializerLength::computeLength, translationUnit); 391 if (!useNewAST) { 392 Stats::Time::TimeCall("Array Length", 393 ArrayLength::computeLength, translationUnit); 394 } 386 395 Stats::Time::TimeCall("Find Special Declarations", 387 396 Validate::findSpecialDecls, translationUnit); … … 1332 1341 } 1333 1342 1343 void InitializerLength::computeLength( std::list< Declaration * > & translationUnit ) { 1344 PassVisitor<InitializerLength> len; 1345 acceptAll( translationUnit, len ); 1346 } 1347 1334 1348 void ArrayLength::computeLength( std::list< Declaration * > & translationUnit ) { 1335 1349 PassVisitor<ArrayLength> len; … … 1337 1351 } 1338 1352 1339 void ArrayLength::previsit( ObjectDecl * objDecl ) {1353 void InitializerLength::previsit( ObjectDecl * objDecl ) { 1340 1354 if ( ArrayType * at = dynamic_cast< ArrayType * >( objDecl->type ) ) { 1341 1355 if ( at->dimension ) return; … … 1347 1361 1348 1362 void ArrayLength::previsit( ArrayType * type ) { 1349 if (!useNewAST) { 1350 if ( type->dimension ) { 1351 // need to resolve array dimensions early so that constructor code can correctly determine 1352 // if a type is a VLA (and hence whether its elements need to be constructed) 1353 ResolvExpr::findSingleExpression( type->dimension, Validate::SizeType->clone(), indexer ); 1354 1355 // must re-evaluate whether a type is a VLA, now that more information is available 1356 // (e.g. the dimension may have been an enumerator, which was unknown prior to this step) 1357 type->isVarLen = ! InitTweak::isConstExpr( type->dimension ); 1358 } 1363 if ( type->dimension ) { 1364 // need to resolve array dimensions early so that constructor code can correctly determine 1365 // if a type is a VLA (and hence whether its elements need to be constructed) 1366 ResolvExpr::findSingleExpression( type->dimension, Validate::SizeType->clone(), indexer ); 1367 1368 // must re-evaluate whether a type is a VLA, now that more information is available 1369 // (e.g. the dimension may have been an enumerator, which was unknown prior to this step) 1370 type->isVarLen = ! InitTweak::isConstExpr( type->dimension ); 1359 1371 } 1360 1372 } … … 1462 1474 } 1463 1475 } 1476 1477 /* 1464 1478 1465 1479 /// Associates forward declarations of aggregates with their definitions … … 1844 1858 } 1845 1859 }; 1860 */ 1846 1861 } // anonymous namespace 1847 1862 1863 /* 1848 1864 const ast::Type * validateType( 1849 1865 const CodeLocation & loc, const ast::Type * type, const ast::SymbolTable & symtab ) { … … 1854 1870 return type->accept( lrt )->accept( fpd ); 1855 1871 } 1872 */ 1856 1873 1857 1874 } // namespace SymTab -
src/Tuples/TupleAssignment.cc
r42f6e07 r2b4daf2 504 504 505 505 std::vector< ast::ptr< ast::Expr > > match() override { 506 // temporary workaround for new and old ast to coexist and avoid name collision 507 static UniqueName lhsNamer( "__massassign_Ln" ); 508 static UniqueName rhsNamer( "__massassign_Rn" ); 506 static UniqueName lhsNamer( "__massassign_L" ); 507 static UniqueName rhsNamer( "__massassign_R" ); 509 508 // empty tuple case falls into this matcher 510 509 assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 ); … … 535 534 536 535 std::vector< ast::ptr< ast::Expr > > match() override { 537 // temporary workaround for new and old ast to coexist and avoid name collision 538 static UniqueName lhsNamer( "__multassign_Ln" ); 539 static UniqueName rhsNamer( "__multassign_Rn" ); 536 static UniqueName lhsNamer( "__multassign_L" ); 537 static UniqueName rhsNamer( "__multassign_R" ); 540 538 541 539 if ( lhs.size() != rhs.size() ) return {}; -
tests/.expect/KRfunctions.nast.x86.txt
r42f6e07 r2b4daf2 86 86 __attribute__ ((unused)) signed int (*_X11_retval_f11PA0i_1)[]; 87 87 } 88 signed int (*_X3f12FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned longint )10)]{89 __attribute__ ((unused)) signed int (*_X11_retval_f12PA0A0i_1)[][((unsigned longint )10)];88 signed int (*_X3f12FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned int )10)]{ 89 __attribute__ ((unused)) signed int (*_X11_retval_f12PA0A0i_1)[][((unsigned int )10)]; 90 90 } 91 signed int (*_X3f13FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned longint )10)]{92 __attribute__ ((unused)) signed int (*_X11_retval_f13PA0A0i_1)[][((unsigned longint )10)];91 signed int (*_X3f13FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned int )10)]{ 92 __attribute__ ((unused)) signed int (*_X11_retval_f13PA0A0i_1)[][((unsigned int )10)]; 93 93 } 94 signed int (*_X3f14FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned longint )10)]{95 __attribute__ ((unused)) signed int (*_X11_retval_f14PA0A0i_1)[][((unsigned longint )10)];94 signed int (*_X3f14FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned int )10)]{ 95 __attribute__ ((unused)) signed int (*_X11_retval_f14PA0A0i_1)[][((unsigned int )10)]; 96 96 } 97 97 signed int _X3f15Fi_iii__1(signed int _X1ai_1, signed int _X1bi_1, signed int _X1ci_1){ -
tests/.expect/attributes.nast.x86.txt
r42f6e07 r2b4daf2 623 623 __attribute__ ((used,used,used,used)) const signed int *_X3vd3PKi_1; 624 624 __attribute__ ((used,used,unused,used,unused)) const signed int *_X3vd4PKi_1; 625 __attribute__ ((used,used,used)) const signed int _X3vd5A0Ki_1[((unsigned longint )5)];626 __attribute__ ((used,used,unused,used)) const signed int _X3vd6A0Ki_1[((unsigned longint )5)];625 __attribute__ ((used,used,used)) const signed int _X3vd5A0Ki_1[((unsigned int )5)]; 626 __attribute__ ((used,used,unused,used)) const signed int _X3vd6A0Ki_1[((unsigned int )5)]; 627 627 __attribute__ ((used,used,used,used)) const signed int (*_X3vd7Fi___1)(); 628 628 __attribute__ ((used,used,unused,used,used)) const signed int (*_X3vd8Fi___1)(); … … 647 647 __attribute__ ((unused,unused,used)) signed int _X2t1i_2; 648 648 __attribute__ ((unused,unused,unused,unused,unused)) signed int **_X2t2PPi_2; 649 __attribute__ ((unused,unused,unused)) signed int _X2t3A0i_2[((unsigned longint )5)];650 __attribute__ ((unused,unused,unused,unused,unused)) signed int **_X2t4A0PPi_2[((unsigned longint )5)];649 __attribute__ ((unused,unused,unused)) signed int _X2t3A0i_2[((unsigned int )5)]; 650 __attribute__ ((unused,unused,unused,unused,unused)) signed int **_X2t4A0PPi_2[((unsigned int )5)]; 651 651 __attribute__ ((unused,unused,unused)) signed int _X2t5Fi___2(); 652 652 __attribute__ ((unused,unused,unused,unused)) signed int *_X2t6FPi___2(); … … 671 671 signed int _X4tpr2Fi_PPi__1(__attribute__ ((unused,unused,unused,unused,unused,unused)) signed int **_X3FooPPi_1); 672 672 signed int _X4tpr3Fi_Pi__1(__attribute__ ((unused,unused,unused)) signed int *_X3FooPi_1); 673 signed int _X4tpr4Fi_Fi_Pi___1(__attribute__ ((unused,unused)) signed int (*__anonymous_object1)(signed int __param_0[((unsigned longint )5)]));673 signed int _X4tpr4Fi_Fi_Pi___1(__attribute__ ((unused,unused)) signed int (*__anonymous_object1)(signed int __param_0[((unsigned int )5)])); 674 674 signed int _X4tpr5Fi_Fi____1(__attribute__ ((unused,unused,unused)) signed int (*_X3FooFi___1)()); 675 675 signed int _X4tpr6Fi_Fi____1(__attribute__ ((unused,unused,unused)) signed int (*_X3FooFi___1)()); … … 679 679 __attribute__ ((used,unused)) signed int _X3ad1i_2; 680 680 __attribute__ ((unused,unused,unused)) signed int *_X3ad2Pi_2; 681 __attribute__ ((unused,unused,unused)) signed int _X3ad3A0i_2[((unsigned longint )5)];682 __attribute__ ((unused,unused,unused,unused,unused)) signed int (*_X3ad4PA0i_2)[((unsigned longint )10)];681 __attribute__ ((unused,unused,unused)) signed int _X3ad3A0i_2[((unsigned int )5)]; 682 __attribute__ ((unused,unused,unused,unused,unused)) signed int (*_X3ad4PA0i_2)[((unsigned int )10)]; 683 683 __attribute__ ((unused,unused,unused,unused,used)) signed int _X3ad5i_2; 684 684 __attribute__ ((unused,unused,unused,unused,unused)) signed int _X3ad6Fi___2(); -
tests/.expect/functions.nast.x86.txt
r42f6e07 r2b4daf2 46 46 __attribute__ ((unused)) signed int (*_X11_retval_f10PA0i_1)[]; 47 47 } 48 signed int (*_X3f11FPA0A0i___1())[][((unsigned longint )3)]{49 __attribute__ ((unused)) signed int (*_X11_retval_f11PA0A0i_1)[][((unsigned longint )3)];50 } 51 signed int (*_X3f12FPA0A0i___1())[][((unsigned longint )3)]{52 __attribute__ ((unused)) signed int (*_X11_retval_f12PA0A0i_1)[][((unsigned longint )3)];48 signed int (*_X3f11FPA0A0i___1())[][((unsigned int )3)]{ 49 __attribute__ ((unused)) signed int (*_X11_retval_f11PA0A0i_1)[][((unsigned int )3)]; 50 } 51 signed int (*_X3f12FPA0A0i___1())[][((unsigned int )3)]{ 52 __attribute__ ((unused)) signed int (*_X11_retval_f12PA0A0i_1)[][((unsigned int )3)]; 53 53 } 54 54 signed int _X4fII1Fi_i__1(signed int _X1ii_1){ … … 250 250 signed int _X1fFi_Fi_ii_Fi_i___1(__attribute__ ((unused)) signed int (*__anonymous_object20)(signed int __param_0, signed int __param_1), __attribute__ ((unused)) signed int (*__anonymous_object21)(signed int __param_0)){ 251 251 __attribute__ ((unused)) signed int _X9_retval_fi_1; 252 signed int (*(*_X2pcPA0A0PA0A0i_2)[][((unsigned long int )10)])[][((unsigned longint )3)];253 signed int (*(*_X1pPA0A0PA0A0i_2)[][((unsigned long int )10)])[][((unsigned longint )3)];252 signed int (*(*_X2pcPA0A0PA0A0i_2)[][((unsigned int )10)])[][((unsigned int )3)]; 253 signed int (*(*_X1pPA0A0PA0A0i_2)[][((unsigned int )10)])[][((unsigned int )3)]; 254 254 signed int (*(*_X1pPA0Fi_i__2)[])(signed int __param_0); 255 255 } -
tests/Makefile.am
r42f6e07 r2b4daf2 103 103 104 104 mostlyclean-local : 105 find ${builddir} -not -path './__pycache__/*' -path '*.o' -delete 106 find ${builddir} -not -path './__pycache__/*' -path '*/.err/*.log' -delete 107 find ${builddir} -not -path './__pycache__/*' -path '*/.out/*.log' -delete 105 108 rm -f ${EXTRA_PROGRAMS} 106 109 rm -rf __pycache__ 107 find ${builddir} -path '*.o' -delete108 find ${builddir} -path '*/.err/*.log' -delete109 find ${builddir} -path '*/.out/*.log' -delete110 110 111 111 distclean-local : -
tests/concurrent/futures/.expect/basic.txt
r42f6e07 r2b4daf2 1 start 1 2 done -
tests/concurrent/futures/basic.cfa
r42f6e07 r2b4daf2 1 1 #include <thread.hfa> 2 2 3 enum {NFUTURES = 10}; 3 4 … … 83 84 84 85 int main() { 86 printf( "start\n" ); // non-empty .expect file 85 87 processor procs[2]; 86 88 { -
tests/errors/.expect/completeType.nast.x64.txt
r42f6e07 r2b4daf2 12 12 Application of 13 13 Variable Expression: *?: forall 14 DT: data type14 instance of type DT (not function type) 15 15 function 16 16 ... with parameters … … 21 21 ... with resolved type: 22 22 pointer to forall 23 [unbound]:data type23 instance of type [unbound] (not function type) 24 24 function 25 25 ... with parameters … … 41 41 void 42 42 ) 43 Environment:([unbound] ) -> instance of struct A without body (no widening)43 Environment:([unbound]DT) -> instance of struct A without body (no widening) 44 44 45 45 … … 47 47 Application of 48 48 Variable Expression: *?: forall 49 DT: data type49 instance of type DT (not function type) 50 50 function 51 51 ... with parameters … … 56 56 ... with resolved type: 57 57 pointer to forall 58 [unbound]:data type58 instance of type [unbound] (not function type) 59 59 function 60 60 ... with parameters … … 76 76 void 77 77 ) 78 Environment:([unbound] ) -> instance of struct B with body (no widening)78 Environment:([unbound]DT) -> instance of struct B with body (no widening) 79 79 80 80 … … 113 113 Cost ( 0, 1, 0, 0, 1, -5, 0 ): Application of 114 114 Variable Expression: baz: forall 115 T: sized data type 116 ... with assertions 117 ?=?: pointer to function 115 instance of type T (not function type) 116 with assertions 117 Variable Expression: ?=?: pointer to function 118 ... with parameters 119 reference to instance of type T (not function type) 120 instance of type T (not function type) 121 ... returning 122 instance of type T (not function type) 123 124 ... with resolved type: 125 pointer to function 118 126 ... with parameters 119 127 reference to instance of type T (not function type) … … 122 130 instance of type T (not function type) 123 131 124 ?{}: pointer to function 125 ... with parameters 126 reference to instance of type T (not function type) 127 ... returning nothing 128 129 ?{}: pointer to function 130 ... with parameters 131 reference to instance of type T (not function type) 132 instance of type T (not function type) 133 ... returning nothing 134 135 ^?{}: pointer to function 136 ... with parameters 137 reference to instance of type T (not function type) 138 ... returning nothing 139 132 Variable Expression: ?{}: pointer to function 133 ... with parameters 134 reference to instance of type T (not function type) 135 ... returning nothing 136 137 ... with resolved type: 138 pointer to function 139 ... with parameters 140 reference to instance of type T (not function type) 141 ... returning nothing 142 143 Variable Expression: ?{}: pointer to function 144 ... with parameters 145 reference to instance of type T (not function type) 146 instance of type T (not function type) 147 ... returning nothing 148 149 ... with resolved type: 150 pointer to function 151 ... with parameters 152 reference to instance of type T (not function type) 153 instance of type T (not function type) 154 ... returning nothing 155 156 Variable Expression: ^?{}: pointer to function 157 ... with parameters 158 reference to instance of type T (not function type) 159 ... returning nothing 160 161 ... with resolved type: 162 pointer to function 163 ... with parameters 164 reference to instance of type T (not function type) 165 ... returning nothing 140 166 141 167 function … … 146 172 ... with resolved type: 147 173 pointer to forall 148 [unbound]:sized data type 149 ... with assertions 150 ?=?: pointer to function 174 instance of type [unbound] (not function type) 175 with assertions 176 Variable Expression: ?=?: pointer to function 177 ... with parameters 178 reference to instance of type T (not function type) 179 instance of type T (not function type) 180 ... returning 181 instance of type T (not function type) 182 183 ... with resolved type: 184 pointer to function 151 185 ... with parameters 152 186 reference to instance of type [unbound] (not function type) … … 155 189 instance of type [unbound] (not function type) 156 190 157 ?{}: pointer to function 191 Variable Expression: ?{}: pointer to function 192 ... with parameters 193 reference to instance of type T (not function type) 194 ... returning nothing 195 196 ... with resolved type: 197 pointer to function 158 198 ... with parameters 159 199 reference to instance of type [unbound] (not function type) 160 200 ... returning nothing 161 201 162 ?{}: pointer to function 202 Variable Expression: ?{}: pointer to function 203 ... with parameters 204 reference to instance of type T (not function type) 205 instance of type T (not function type) 206 ... returning nothing 207 208 ... with resolved type: 209 pointer to function 163 210 ... with parameters 164 211 reference to instance of type [unbound] (not function type) … … 166 213 ... returning nothing 167 214 168 ^?{}: pointer to function 215 Variable Expression: ^?{}: pointer to function 216 ... with parameters 217 reference to instance of type T (not function type) 218 ... returning nothing 219 220 ... with resolved type: 221 pointer to function 169 222 ... with parameters 170 223 reference to instance of type [unbound] (not function type) 171 224 ... returning nothing 172 173 225 174 226 function … … 188 240 void 189 241 ) 190 Environment:([unbound] ) -> instance of type T (not function type) (no widening)242 Environment:([unbound]T) -> instance of type T (not function type) (no widening) 191 243 192 244 Could not satisfy assertion: 193 ?=?: pointer to function245 Variable Expression: ?=?: pointer to function 194 246 ... with parameters 195 reference to instance of type [unbound](not function type)196 instance of type [unbound](not function type)247 reference to instance of type T (not function type) 248 instance of type T (not function type) 197 249 ... returning 198 instance of type [unbound] (not function type) 199 250 instance of type T (not function type) 251 252 ... with resolved type: 253 pointer to function 254 ... with parameters 255 reference to instance of type [unbound] (not function type) 256 instance of type [unbound] (not function type) 257 ... returning 258 instance of type [unbound] (not function type) 259 -
tests/errors/.expect/completeType.nast.x86.txt
r42f6e07 r2b4daf2 12 12 Application of 13 13 Variable Expression: *?: forall 14 DT: data type14 instance of type DT (not function type) 15 15 function 16 16 ... with parameters … … 21 21 ... with resolved type: 22 22 pointer to forall 23 [unbound]:data type23 instance of type [unbound] (not function type) 24 24 function 25 25 ... with parameters … … 41 41 void 42 42 ) 43 Environment:([unbound] ) -> instance of struct A without body (no widening)43 Environment:([unbound]DT) -> instance of struct A without body (no widening) 44 44 45 45 … … 47 47 Application of 48 48 Variable Expression: *?: forall 49 DT: data type49 instance of type DT (not function type) 50 50 function 51 51 ... with parameters … … 56 56 ... with resolved type: 57 57 pointer to forall 58 [unbound]:data type58 instance of type [unbound] (not function type) 59 59 function 60 60 ... with parameters … … 76 76 void 77 77 ) 78 Environment:([unbound] ) -> instance of struct B with body (no widening)78 Environment:([unbound]DT) -> instance of struct B with body (no widening) 79 79 80 80 … … 113 113 Cost ( 0, 1, 0, 0, 1, -5, 0 ): Application of 114 114 Variable Expression: baz: forall 115 T: sized data type 116 ... with assertions 117 ?=?: pointer to function 115 instance of type T (not function type) 116 with assertions 117 Variable Expression: ?=?: pointer to function 118 ... with parameters 119 reference to instance of type T (not function type) 120 instance of type T (not function type) 121 ... returning 122 instance of type T (not function type) 123 124 ... with resolved type: 125 pointer to function 118 126 ... with parameters 119 127 reference to instance of type T (not function type) … … 122 130 instance of type T (not function type) 123 131 124 ?{}: pointer to function 125 ... with parameters 126 reference to instance of type T (not function type) 127 ... returning nothing 128 129 ?{}: pointer to function 130 ... with parameters 131 reference to instance of type T (not function type) 132 instance of type T (not function type) 133 ... returning nothing 134 135 ^?{}: pointer to function 136 ... with parameters 137 reference to instance of type T (not function type) 138 ... returning nothing 139 132 Variable Expression: ?{}: pointer to function 133 ... with parameters 134 reference to instance of type T (not function type) 135 ... returning nothing 136 137 ... with resolved type: 138 pointer to function 139 ... with parameters 140 reference to instance of type T (not function type) 141 ... returning nothing 142 143 Variable Expression: ?{}: pointer to function 144 ... with parameters 145 reference to instance of type T (not function type) 146 instance of type T (not function type) 147 ... returning nothing 148 149 ... with resolved type: 150 pointer to function 151 ... with parameters 152 reference to instance of type T (not function type) 153 instance of type T (not function type) 154 ... returning nothing 155 156 Variable Expression: ^?{}: pointer to function 157 ... with parameters 158 reference to instance of type T (not function type) 159 ... returning nothing 160 161 ... with resolved type: 162 pointer to function 163 ... with parameters 164 reference to instance of type T (not function type) 165 ... returning nothing 140 166 141 167 function … … 146 172 ... with resolved type: 147 173 pointer to forall 148 [unbound]:sized data type 149 ... with assertions 150 ?=?: pointer to function 174 instance of type [unbound] (not function type) 175 with assertions 176 Variable Expression: ?=?: pointer to function 177 ... with parameters 178 reference to instance of type T (not function type) 179 instance of type T (not function type) 180 ... returning 181 instance of type T (not function type) 182 183 ... with resolved type: 184 pointer to function 151 185 ... with parameters 152 186 reference to instance of type [unbound] (not function type) … … 155 189 instance of type [unbound] (not function type) 156 190 157 ?{}: pointer to function 191 Variable Expression: ?{}: pointer to function 192 ... with parameters 193 reference to instance of type T (not function type) 194 ... returning nothing 195 196 ... with resolved type: 197 pointer to function 158 198 ... with parameters 159 199 reference to instance of type [unbound] (not function type) 160 200 ... returning nothing 161 201 162 ?{}: pointer to function 202 Variable Expression: ?{}: pointer to function 203 ... with parameters 204 reference to instance of type T (not function type) 205 instance of type T (not function type) 206 ... returning nothing 207 208 ... with resolved type: 209 pointer to function 163 210 ... with parameters 164 211 reference to instance of type [unbound] (not function type) … … 166 213 ... returning nothing 167 214 168 ^?{}: pointer to function 215 Variable Expression: ^?{}: pointer to function 216 ... with parameters 217 reference to instance of type T (not function type) 218 ... returning nothing 219 220 ... with resolved type: 221 pointer to function 169 222 ... with parameters 170 223 reference to instance of type [unbound] (not function type) 171 224 ... returning nothing 172 173 225 174 226 function … … 188 240 void 189 241 ) 190 Environment:([unbound] ) -> instance of type T (not function type) (no widening)242 Environment:([unbound]T) -> instance of type T (not function type) (no widening) 191 243 192 244 Could not satisfy assertion: 193 ?=?: pointer to function245 Variable Expression: ?=?: pointer to function 194 246 ... with parameters 195 reference to instance of type [unbound](not function type)196 instance of type [unbound](not function type)247 reference to instance of type T (not function type) 248 instance of type T (not function type) 197 249 ... returning 198 instance of type [unbound] (not function type) 199 250 instance of type T (not function type) 251 252 ... with resolved type: 253 pointer to function 254 ... with parameters 255 reference to instance of type [unbound] (not function type) 256 instance of type [unbound] (not function type) 257 ... returning 258 instance of type [unbound] (not function type) 259 -
tests/raii/.expect/ctor-autogen-ERR1.nast.txt
r42f6e07 r2b4daf2 70 70 ... with environment: 71 71 Types: 72 Non-types:73 72 74 73
Note: See TracChangeset
for help on using the changeset viewer.