Changes in / [2b4daf2:42f6e07]
- Files:
-
- 11 added
- 57 deleted
- 87 edited
Legend:
- Unmodified
- Added
- Removed
-
Jenkins/FullBuild
r2b4daf2 r42f6e07 32 32 33 33 stage('Package') { 34 trigger_dist( commitId, currentBuild.number.toString() )34 build job: 'Cforall_Distribute_Ref', parameters: [string(name: 'GitRef', value: commitId), string(name: 'Build', value: 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: false112 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 121 105 //=========================================================================================================== 122 106 //Routine responsible of sending the email notification once the build is completed -
Jenkins/tools.groovy
r2b4daf2 r42f6e07 6 6 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils 7 7 8 // Global for the stage name 9 StageName = '' 10 8 11 // wrapper around stage declaretion to be more verbose 9 12 // and allow showing as skipped in the UI 10 13 def BuildStage(String name, boolean run, Closure block ) { 11 echo " -------- ${name} -------- " 14 StageName = name 15 echo " -------- ${StageName} -------- " 12 16 if(run) { 13 17 stage(name, block) -
Jenkinsfile
r2b4daf2 r42f6e07 54 54 //attach the build log to the email 55 55 catch (Exception caughtError) { 56 // Store the result of the build log 57 currentBuild.result = "FAILURE" 58 59 // An error has occured, the build log is relevent 56 //rethrow error later 57 err = caughtError 58 59 echo err.toString() 60 61 //An error has occured, the build log is relevent 60 62 log_needed = true 61 63 62 // rethrow error later 63 err = caughtError 64 65 // print the error so it shows in the log 66 echo err.toString() 64 //Store the result of the build log 65 currentBuild.result = "${tools.StageName} FAILURE".trim() 67 66 } 68 67 -
benchmark/Makefile.am
r2b4daf2 r42f6e07 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.rs528 cd $(builddir) && cargo build --release529 cp $(builddir)/target/release/$(basename $@) $@ -
benchmark/creation/node_cor.js
r2b4daf2 r42f6e07 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
r2b4daf2 r42f6e07 11 11 cor = coroutine() 12 12 13 for ( var i = 0; i < times; i += 1 ) { // warm JIT13 for ( var i = 0; i < times; i += 1 ) { // warm git 14 14 cor.next(); 15 15 } -
benchmark/readyQ/bench.go
r2b4daf2 r42f6e07 5 5 "flag" 6 6 "fmt" 7 "log"8 7 "os" 9 8 "runtime" 10 "runtime/pprof"11 9 "sync/atomic" 12 10 "time" … … 45 43 } 46 44 47 func bench_init() func(){45 func bench_init() { 48 46 nprocsOpt := flag.Int("p", 1, "The number of processors") 49 47 nthreadsOpt := flag.Int("t", 1, "The number of threads") 50 48 durationOpt := flag.Float64("d", 0, "Duration of the experiment in seconds") 51 49 stopOpt := flag.Uint64("i", 0, "Duration of the experiment in iterations") 52 cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file")53 50 54 51 flag.Parse() … … 75 72 76 73 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 }91 74 } -
benchmark/readyQ/cycle.cpp
r2b4daf2 r42f6e07 71 71 { 'r', "ringsize", "Number of threads in a cycle", ring_size } 72 72 }; 73 BENCH_OPT_PARSE(" libfibrecycle benchmark");73 BENCH_OPT_PARSE("cforall cycle benchmark"); 74 74 75 75 { -
benchmark/readyQ/cycle.rs
r2b4daf2 r42f6e07 1 #[cfg(any( 2 feature = "sync time rt-threaded", 3 ))] 4 5 extern crate tokio; 6 7 use std::io::{self, Write}; 1 8 use std::sync::Arc; 2 use std::sync::atomic:: Ordering;3 use std::time:: Instant;9 use std::sync::atomic::{AtomicU64, AtomicBool,Ordering}; 10 use std::time::{Instant,Duration}; 4 11 5 12 use tokio::runtime::Builder; 6 13 use tokio::sync; 7 14 use tokio::time; 15 16 extern crate isatty; 17 use isatty::stdout_isatty; 18 19 extern crate num_format; 20 use num_format::{Locale, ToFormattedString}; 21 22 extern crate clap; 8 23 use clap::{Arg, App}; 9 use num_format::{Locale, ToFormattedString}; 10 11 #[path = "../bench.rs"] 12 mod bench; 13 14 // ================================================== 24 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 15 66 struct Partner { 16 67 sem: sync::Semaphore, … … 18 69 } 19 70 20 async fn partner_main( idx: usize, others: Arc<Vec<Arc<Partner>>>, exp: Arc<bench::BenchData> ) -> u64{71 async fn partner_main(result: sync::oneshot::Sender<u64>, idx: usize, others: Arc<Vec<Arc<Partner>>> ) { 21 72 let this = &others[idx]; 22 73 let mut count:u64 = 0; … … 26 77 count += 1; 27 78 28 if exp.clock_mode && exp.stop.load(Ordering::Relaxed) { break; } 29 if !exp.clock_mode && count >= exp.stop_count { break; } 30 } 31 32 exp.threads_left.fetch_sub(1, Ordering::SeqCst); 33 count 34 } 35 36 // ================================================== 79 if *CLOCK_MODE && STOP.load(Ordering::Relaxed) { break; } 80 if !*CLOCK_MODE && count >= *STOP_COUNT { break; } 81 } 82 83 THREADS_LEFT.fetch_sub(1, Ordering::SeqCst); 84 result.send( count ).unwrap(); 85 } 86 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 37 116 fn main() { 38 117 let options = App::new("Cycle Tokio") 39 .args(&bench::args()) 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")) 40 122 .arg(Arg::with_name("ringsize") .short("r").long("ringsize") .takes_value(true).default_value("1").help("Number of threads in a cycle")) 41 123 .get_matches(); … … 45 127 let nprocs = options.value_of("nprocs").unwrap().parse::<usize>().unwrap(); 46 128 47 let tthreads = nthreads * ring_size; 48 let exp = Arc::new(bench::BenchData::new(options, tthreads)); 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 } 49 141 50 142 let s = (1000000 as u64).to_formatted_string(&Locale::en); 51 143 assert_eq!(&s, "1,000,000"); 52 144 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 ); 145 146 let tthreads = nthreads * ring_size; 147 THREADS_LEFT.store(tthreads as u64, Ordering::SeqCst); 148 let thddata = Arc::new(prep(nthreads, tthreads)); 62 149 63 150 let mut global_counter :u64 = 0; … … 70 157 71 158 runtime.block_on(async { 72 let threads: Vec<_> = (0..tthreads).map(|i| { 73 tokio::spawn(partner_main(i, thddata.clone(), exp.clone())) 74 }).collect(); 75 println!("Starting"); 76 77 let start = Instant::now(); 78 79 for i in 0..nthreads { 80 thddata[i].sem.add_permits(1); 81 } 82 83 duration = exp.wait(&start).await; 84 85 println!("\nDone"); 86 87 for i in 0..tthreads { 88 thddata[i].sem.add_permits(1); 89 } 90 91 for t in threads { 92 global_counter += t.await.unwrap(); 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"); 168 169 let is_tty = stdout_isatty(); 170 let start = Instant::now(); 171 172 for i in 0..nthreads { 173 thddata[i].sem.add_permits(1); 174 } 175 176 wait(&start, is_tty).await; 177 178 STOP.store(true, Ordering::SeqCst); 179 duration = start.elapsed(); 180 181 println!("\nDone"); 182 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 } 93 190 } 94 191 }); -
benchmark/readyQ/locality.go
r2b4daf2 r42f6e07 2 2 3 3 import ( 4 "context"5 4 "flag" 6 5 "fmt" 7 6 "math/rand" 8 7 "os" 9 "syscall"10 8 "sync/atomic" 11 9 "time" 12 "unsafe"13 "golang.org/x/sync/semaphore"14 10 "golang.org/x/text/language" 15 11 "golang.org/x/text/message" 16 12 ) 17 13 18 // ================================================== 19 type MyData struct { 20 _p1 [16]uint64 // padding 21 ttid int 22 id int 23 data [] uint64 24 _p2 [16]uint64 // padding 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 } 25 37 } 26 38 27 func NewData(id int, size uint64) (*MyData) {39 func local(result chan uint64, start chan struct{}, stop chan struct{}, size uint64, cnt uint64, channels []chan [] uint64, chan_cnt uint64, share bool) { 28 40 var data [] uint64 29 41 data = make([]uint64, size) … … 31 43 data[i] = 0 32 44 } 33 return &MyData{[16]uint64{0}, syscall.Gettid(), id, data,[16]uint64{0}} 45 count := uint64(0) 46 <- start 47 for true { 48 for i := uint64(0); i < cnt; i++ { 49 data[rand.Uint64() % size] += 1 50 } 51 52 i := rand.Uint64() % chan_cnt 53 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 61 atomic.AddInt64(&threads_left, -1); 62 result <- count 34 63 } 35 64 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 } 65 func main() { 43 66 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 185 <- start 186 187 // Main loop 188 for true { 189 // 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)) 194 var closed bool 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 213 atomic.AddInt64(&threads_left, -1); 214 215 // return result 216 result <- r 217 } 218 219 // ================================================== 220 // Program main 221 func main() { 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)") 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") 226 69 shareOpt := flag.Bool ("s", false, "Pass the work data to the next thread when blocking") 227 70 228 // General benchmark initialization and deinitialization 229 defer bench_init()() 71 bench_init() 230 72 231 // Eval command line arguments232 nspots:= *nspotsOpt233 73 size := *work_sizeOpt 234 74 cnt := *countOpt 235 75 share := *shareOpt 236 76 237 if nspots == 0 { nspots = nthreads - nprocs; }238 239 // Check params240 77 if ! (nthreads > nprocs) { 241 78 fmt.Fprintf(os.Stderr, "Must have more threads than procs\n") … … 243 80 } 244 81 245 // Make global data246 barrierSt art := make(chan struct{}) // Barrier used at the start247 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 results249 channels := make([] Spot, nspots) // Number of spots82 barrierStart := make(chan struct{}) 83 barrierStop := make(chan struct{}) 84 threads_left = int64(nthreads) 85 result := make(chan uint64) 86 channels := make([]chan [] uint64, nthreads - nprocs) 250 87 for i := range channels { 251 channels[i] = Spot{[16]uint64{0},uintptr(0), i,[16]uint64{0}} // init spots88 channels[i] = make(chan [] uint64, 1) 252 89 } 253 90 254 // start the goroutines255 91 for i := 0; i < nthreads; i++ { 256 go local(result, barrierStart, size, cnt, channels, share, i)92 go local(result, barrierStart, barrierStop, size, cnt, channels, uint64(nthreads - nprocs), share) 257 93 } 258 94 fmt.Printf("Starting\n"); 259 95 260 atomic.StoreInt32(&stop, 0)261 96 start := time.Now() 262 close(barrierStart) // release barrier97 close(barrierStart) 263 98 264 wait(start, true); // general benchmark wait99 wait(start, true); 265 100 266 atomic.StoreInt32(&stop, 1)101 close(barrierStop) 267 102 end := time.Now() 268 103 delta := end.Sub(start) … … 270 105 fmt.Printf("\nDone\n") 271 106 272 // release all the blocked threads273 for i := range channels{274 channels[i].release()107 global_counter := uint64(0) 108 for i := 0; i < nthreads; i++ { 109 global_counter += <- result 275 110 } 276 111 277 // Join and accumulate results278 results := NewResult()279 for i := 0; i < nthreads; i++ {280 r := <- result281 results.count += r.count282 results.gmigs += r.gmigs283 results.dmigs += r.dmigs284 }285 286 // Print with nice 's, i.e. 1'000'000 instead of 1000000287 112 p := message.NewPrinter(language.English) 288 p.Printf("Duration ( s): %f\n", delta.Seconds());113 p.Printf("Duration (ms) : %f\n", delta.Seconds()); 289 114 p.Printf("Number of processors : %d\n", nprocs); 290 115 p.Printf("Number of threads : %d\n", nthreads); 291 116 p.Printf("Work size (64bit words): %d\n", size); 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))) 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))) 301 124 } -
benchmark/readyQ/rq_bench.hfa
r2b4daf2 r42f6e07 39 39 } else if(stop_count > 0) { \ 40 40 clock_mode = false; \ 41 printf("Running for %l lu iterations\n", stop_count); \41 printf("Running for %lu iterations\n", stop_count); \ 42 42 } else { \ 43 43 duration = 5; clock_mode = true;\ -
benchmark/readyQ/rq_bench.hpp
r2b4daf2 r42f6e07 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 113 99 bool parse_settrue (const char *, bool & value ) { 114 100 value = true; … … 240 226 { 241 227 int idx = 0; 242 for( size_t i = 0; i < opt_count; i++) {228 for(int i = 0; i < opt_count; i++) { 243 229 if(options[i].long_name) { 244 230 optarr[idx].name = options[i].long_name; … … 270 256 { 271 257 int idx = 0; 272 for( size_t i = 0; i < opt_count; i++) {258 for(int i = 0; i < opt_count; i++) { 273 259 optstring[idx] = options[i].short_name; 274 260 idx++; … … 293 279 case 'h': 294 280 out = stdout; 295 [[fallthrough]];296 281 case '?': 297 282 usage(argv[0], options, opt_count, usage_msg, out); 298 283 default: 299 for( size_t i = 0; i < opt_count; i++) {284 for(int i = 0; i < opt_count; i++) { 300 285 if(opt == options[i].short_name) { 301 286 const char * arg = optarg ? optarg : ""; 302 if( arg[0] == '=' ) { arg++; }303 287 bool success = options[i].parse_fun( arg, options[i].variable ); 304 288 if(success) goto NEXT_ARG; … … 335 319 int width = 0; 336 320 { 337 for( size_t i = 0; i < opt_count; i++) {321 for(int i = 0; i < opt_count; i++) { 338 322 if(options[i].long_name) { 339 323 int w = strlen(options[i].long_name); … … 354 338 fprintf(out, "Usage:\n %s %s\n", cmd, help); 355 339 356 for( size_t i = 0; i < opt_count; i++) {340 for(int i = 0; i < opt_count; i++) { 357 341 printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help); 358 342 } -
configure.ac
r2b4daf2 r42f6e07 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 ]) 305 306 AC_OUTPUT(benchmark/Cargo.toml) 307 ]) 304 ])]) 308 305 309 306 AC_CONFIG_LINKS([tests/test.py:tests/test.py]) -
doc/theses/thierry_delisle_PhD/thesis/Makefile
r2b4daf2 r42f6e07 28 28 base \ 29 29 empty \ 30 emptybit \31 emptytls \32 emptytree \33 fairness \34 30 system \ 35 31 } … … 77 73 ${LaTeX} $< 78 74 79 build/fairness.svg : fig/fairness.py | ${Build}80 python3 $< $@81 82 75 ## Define the default recipes. 83 76 … … 95 88 fig2dev -L pstex_t -p ${Build}/$@ $< > ${Build}/$@_t 96 89 97 %.pstex : build/%.svg | ${Build}98 inkscape -z -D --file=$< --export-eps=${Build}/$@ --export-latex99 mv ${Build}/$@_tex ${Build}/$@_t100 echo "sed -i 's/$@/${Build}/$@/g' ${Build}/$@_t"101 sed -i 's/$@/${Build}\/$@/g' ${Build}/$@_t102 103 90 ## pstex with inverted colors 104 91 %.dark.pstex : fig/%.fig Makefile | ${Build} -
doc/theses/thierry_delisle_PhD/thesis/glossary.tex
r2b4daf2 r42f6e07 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}10 9 \newacronym{raii}{RAII}{Resource Acquisition Is Initialization} 11 10 \newacronym{tls}{TLS}{Thread Local Storage} -
doc/theses/thierry_delisle_PhD/thesis/local.bib
r2b4daf2 r42f6e07 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
r2b4daf2 r42f6e07 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 the system has the ressources necessary to accomplish the work, \eg, enough workers. In short, the system is neither overloaded nor 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 but the system has the ressources necessary to accomplish the work. In short, the system is neither overloaded or 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 , \eg, adding or removing workers. Flaws in the scheduling when the system is in the steady state cantherefore 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. Flaws in the scheduling in the steady state tend therefore to be pervasive in all states. 6 6 7 7 \section{Design Goals} 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 respectthe mental model, the system will also respect this model.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 respects 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 twoguarantees: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 2 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 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.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. 27 27 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} 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}. 50 30 51 31 \section{Design} 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. 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 53 33 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. 34 \subsection{Sharding} 56 35 57 36 \begin{figure} … … 66 45 67 46 \subsection{Finding threads} 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. 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. 69 50 70 51 … … 80 61 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. 81 62 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: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. 83 64 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} 65 \paragraph{Dense Information} -
doc/theses/thierry_delisle_PhD/thesis/text/existing.tex
r2b4daf2 r42f6e07 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
r2b4daf2 r42f6e07 58 58 concurrency/iofwd.hfa \ 59 59 containers/list.hfa \ 60 containers/stackLockFree.hfa \ 61 vec/vec.hfa \ 62 vec/vec2.hfa \ 63 vec/vec3.hfa \ 64 vec/vec4.hfa 60 containers/stackLockFree.hfa 65 61 66 62 inst_headers_src = \ … … 98 94 concurrency/clib/cfathread.h \ 99 95 concurrency/invoke.h \ 100 concurrency/future.hfa \101 96 concurrency/kernel/fwd.hfa 102 97 -
libcfa/src/bits/collection.hfa
r2b4daf2 r42f6e07 1 1 #pragma once 2 #include <stdio.h> // REMOVE THIS AFTER DEBUGGING3 4 2 5 3 struct Colable { 6 structColable * next; // next node in the list4 Colable * next; // next node in the list 7 5 // invariant: (next != 0) <=> listed() 8 6 }; 9 #ifdef __cforall 10 staticinline {7 8 inline { 11 9 // PUBLIC 12 10 … … 30 28 } 31 29 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 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 38 40 } // distribution 39 41 40 forall( dtype T | { T *& Next ( T * ); } ) {41 bool listed( T * n ) {42 return Next( n ) != 0p;43 }44 }45 42 46 43 struct Collection { … … 48 45 }; 49 46 50 staticinline {47 inline { 51 48 // class invariant: root == 0 & empty() | *root in *this 52 49 void ?{}( Collection &, const Collection & ) = void; // no copy … … 68 65 69 66 struct ColIter { 70 void * curr; // element returned by |67 void * curr; // element to be returned by >> 71 68 }; 72 69 73 staticinline {70 inline { 74 71 void ?{}( ColIter & colIter ) with( colIter ) { 75 72 curr = 0p; … … 82 79 } // distribution 83 80 } // distribution 84 #endif -
libcfa/src/bits/containers.hfa
r2b4daf2 r42f6e07 36 36 #define __small_array_t(T) __small_array(T) 37 37 #else 38 #define __small_array_t(T) __small_array38 #define __small_array_t(T) struct __small_array 39 39 #endif 40 40 -
libcfa/src/bits/defs.hfa
r2b4daf2 r42f6e07 29 29 #define __cfa_anonymous_object(x) inline struct x 30 30 #else 31 #define __cfa_anonymous_object(x) structx __cfa_anonymous_object31 #define __cfa_anonymous_object(x) x __cfa_anonymous_object 32 32 #endif 33 33 -
libcfa/src/bits/locks.hfa
r2b4daf2 r42f6e07 283 283 void ^?{}(future_t &) {} 284 284 285 void reset(future_t & this) {286 // needs to be in 0p or 1p287 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);288 }289 290 285 // check if the future is available 291 286 bool available( future_t & this ) { … … 345 340 346 341 // Mark the future as abandoned, meaning it will be deleted by the server 347 bool abandon( future_t & this ) { 348 /* paranoid */ verify( this.ptr != 3p ); 349 350 // Mark the future as abandonned 342 void abandon( future_t & this ) { 351 343 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 it354 if( got == 0p ) return false;355 344 356 345 // got == 2p: the future is ready but the context hasn't fully been consumed … … 358 347 if( got == 2p ) { 359 348 while( this.ptr != 1p ) Pause(); 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; 349 } 350 return; 367 351 } 368 352 -
libcfa/src/bits/queue.hfa
r2b4daf2 r42f6e07 3 3 #include "bits/collection.hfa" 4 4 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 * ); } ) { 5 forall( dtype T ) { 12 6 struct Queue { 13 7 inline Collection; // Plan 9 inheritance … … 40 34 } // post: n == tail() & succ(n) == 0 | n != tail() & *succ(n) in *q 41 35 42 T &addHead( Queue(T) & q, T & n ) with( q ) {36 void addHead( Queue(T) & q, T & n ) with( q ) { 43 37 #ifdef __CFA_DEBUG__ 44 38 if ( listed( &n ) ) abort( "(Queue &)%p.addHead( %p ) : Node is already on another list.", &q, &n ); … … 51 45 Next( &n ) = &n; // last node points to itself 52 46 } 53 return n;54 47 } 55 48 56 T &addTail( Queue(T) & q, T & n ) with( q ) {49 void addTail( Queue(T) & q, T & n ) with( q ) { 57 50 #ifdef __CFA_DEBUG__ 58 51 if ( listed( &n ) ) abort( "(Queue &)%p.addTail( %p ) : Node is already on another list.", &q, &n ); … … 62 55 last = &n; 63 56 Next( &n ) = &n; // last node points to itself 64 return n;65 57 } 66 58 67 T &add( Queue(T) & q, T & n ) with( q ) {68 returnaddTail( q, n );59 void add( Queue(T) & q, T & n ) with( q ) { 60 addTail( q, n ); 69 61 } 70 62 … … 72 64 T & t = head( q ); 73 65 if ( root ) { 74 root = Next( (T *)root );66 root = Next( root ); 75 67 if ( &head( q ) == &t ) { 76 68 root = last = 0p; // only one element … … 85 77 } 86 78 87 T &remove( Queue(T) & q, T & n ) with( q ) { // O(n)79 void remove( Queue(T) & q, T & n ) with( q ) { // O(n) 88 80 #ifdef __CFA_DEBUG__ 89 81 if ( ! listed( (Colable &)n ) ) abort( "(Queue &)%p.remove( %p ) : Node is not on a list.", &q, &n ); … … 111 103 curr = Next( curr ); 112 104 } 113 return n;114 105 } // post: ! listed( n ) 115 106 116 T & dropTail( Queue(T) & q ) with( q ) { 107 T & dropTail( Queue(T) & q ) with( q ) { // O(n) 117 108 T & n = tail( q ); 118 109 return &n ? remove( q, n ), n : *0p; … … 151 142 } // distribution 152 143 153 forall( dtype T | { T *& Next ( T * ); }) {144 forall( dtype T ) { 154 145 struct QueueIter { 155 146 inline ColIter; // Plan 9 inheritance … … 161 152 } // post: curr == 0p 162 153 163 // create an iterator active in queue q154 // create an iterator active in Queue q 164 155 void ?{}( QueueIter(T) & qi, Queue(T) & q ) with( qi ) { 165 156 curr = &head( q ); … … 170 161 } // post: curr = {e in q} 171 162 172 // make existing iterator active in queue q163 // make existing iterator active in Queue q 173 164 void over( QueueIter(T) & qi, Queue(T) & q ) with( qi ) { 174 165 curr = &head( q ); 175 166 } // post: curr = {e in q} 176 167 177 bool ? |?( QueueIter(T) & qi, T && tp ) with( qi ) {168 bool ?>>?( QueueIter(T) & qi, T && tp ) with( qi ) { 178 169 if ( curr ) { 179 170 &tp = Curr( qi ); … … 183 174 return &tp != 0p; 184 175 } 185 // post: elts == null & !operator |(tp) | elts != null & *tp' in elts & elts' == elts - *tp & operator|(tp)176 // post: elts == null & !operator>>(tp) | elts != null & *tp' in elts & elts' == elts - *tp & operator>>(tp) 186 177 } // distribution 187 178 } // distribution -
libcfa/src/bits/sequence.hfa
r2b4daf2 r42f6e07 2 2 3 3 #include "bits/collection.hfa" 4 #include "bits/defs.hfa"5 4 6 5 struct Seqable { 7 __cfa_anonymous_object(Colable);8 structSeqable * back; // pointer to previous node in the list6 inline Colable; 7 Seqable * back; // pointer to previous node in the list 9 8 }; 10 9 11 #ifdef __cforall 12 static inline { 10 inline { 13 11 // PUBLIC 14 12 … … 28 26 } 29 27 30 // //wrappers to make Collection have T31 //forall( dtype T ) {32 //T *& Back( T * n ) {33 //return (T *)Back( (Seqable *)n );34 //}35 //} // distribution28 // wrappers to make Collection have T 29 forall( dtype T ) { 30 T *& Back( T * n ) { 31 return (T *)Back( (Seqable *)n ); 32 } 33 } // distribution 36 34 } // distribution 37 35 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 * ); } ) { 36 forall( dtype T ) { 46 37 struct Sequence { 47 38 inline Collection; // Plan 9 inheritance 48 39 }; 49 40 50 staticinline {41 inline { 51 42 // wrappers to make Collection have T 52 43 T & head( Sequence(T) & s ) with( s ) { … … 59 50 void ?{}( Sequence(T) & s ) with( s ) { 60 51 ((Collection &)s){}; 61 } // post: isEmpty() 62 63 // Return a pointer to the last sequence element, without removing it. 52 } // post: isEmpty(). 53 54 // Return a pointer to the last sequence element, without removing it. 64 55 T & tail( Sequence(T) & s ) with( s ) { 65 56 return root ? (T &)*Back( &head( s ) ) : *0p; … … 74 65 } // post: n == tail() & succ(n) == 0 | n != tail() & *succ(n) in *s 75 66 76 // Return a pointer to the element before *n, or 0p if list empty.67 // Return a pointer to the element before *n, or 0p if there isn't one. 77 68 T * pred( Sequence(T) & s, T * n ) with( s ) { // pre: *n in *s 78 69 #ifdef __CFA_DEBUG__ … … 80 71 #endif // __CFA_DEBUG__ 81 72 return n == &head( s ) ? 0p : Back( n ); 82 } 83 84 85 // Insert *n into the sequence before *bef, or at the end if bef == 0 p.86 T &insertBef( Sequence(T) & s, T & n, T & bef ) with( s ) { // pre: !n->listed() & *bef in *s73 } // post: n == head() & head(n) == 0 | n != head() & *pred(n) in *s 74 75 76 // Insert *n into the sequence before *bef, or at the end if bef == 0. 77 void insertBef( Sequence(T) & s, T & n, T & bef ) with( s ) { // pre: !n->listed() & *bef in *s 87 78 #ifdef __CFA_DEBUG__ 88 79 if ( listed( &n ) ) abort( "(Sequence &)%p.insertBef( %p, %p ) : Node is already on another list.", &s, n, &bef ); … … 112 103 Next( Back( &n ) ) = &n; 113 104 } // if 114 return n;115 105 } // post: n->listed() & *n in *s & succ(n) == bef 116 106 117 107 118 108 // Insert *n into the sequence after *aft, or at the beginning if aft == 0. 119 T &insertAft( Sequence(T) & s, T & aft, T & n ) with( s ) { // pre: !n->listed() & *aft in *s109 void insertAft( Sequence(T) & s, T & aft, T & n ) with( s ) { // pre: !n->listed() & *aft in *s 120 110 #ifdef __CFA_DEBUG__ 121 111 if ( listed( &n ) ) abort( "(Sequence &)%p.insertAft( %p, %p ) : Node is already on another list.", &s, &aft, &n ); … … 143 133 Next( &aft ) = &n; 144 134 } // if 145 return n; 146 } // post: n->listed() & *n in *s & succ(n) == bef 135 } // post: n->listed() & *n in *s & succ(n) == bef 147 136 148 137 // pre: n->listed() & *n in *s 149 T &remove( Sequence(T) & s, T & n ) with( s ) { // O(1)138 void remove( Sequence(T) & s, T & n ) with( s ) { // O(1) 150 139 #ifdef __CFA_DEBUG__ 151 140 if ( ! listed( &n ) ) abort( "(Sequence &)%p.remove( %p ) : Node is not on a list.", &s, &n ); … … 158 147 Next( Back( &n ) ) = Next( &n ); 159 148 Next( &n ) = Back( &n ) = 0p; 160 return n; 161 } // post: !n->listed() 149 } // post: !n->listed(). 162 150 163 151 // Add an element to the head of the sequence. 164 T & addHead( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 165 return insertAft( s, *0p, n ); 166 } 167 152 void addHead( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 153 insertAft( s, *0p, n ); 154 } 168 155 // Add an element to the tail of the sequence. 169 T & addTail( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 170 return insertBef( s, n, *0p ); 171 } 172 156 void addTail( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 157 insertBef( s, n, *0p ); 158 } 173 159 // Add an element to the tail of the sequence. 174 T & add( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 175 return addTail( s, n ); 176 } 177 160 void add( Sequence(T) & s, T & n ) { // pre: !n->listed(); post: n->listed() & head() == n 161 addTail( s, n ); 162 } 178 163 // Remove and return the head element in the sequence. 179 164 T & dropHead( Sequence(T) & s ) { … … 181 166 return &n ? remove( s, n ), n : *0p; 182 167 } 183 184 168 // Remove and return the head element in the sequence. 185 169 T & drop( Sequence(T) & s ) { 186 170 return dropHead( s ); 187 171 } 188 189 172 // Remove and return the tail element in the sequence. 190 173 T & dropTail( Sequence(T) & s ) { … … 201 184 T * toEnd = Back( &head( s ) ); 202 185 T * fromEnd = Back( &head( from ) ); 203 Back( (T *)root ) = fromEnd;186 Back( root ) = fromEnd; 204 187 Next( fromEnd ) = &head( s ); 205 Back( (T *)from.root ) = toEnd;188 Back( from.root ) = toEnd; 206 189 Next( toEnd ) = &head( from ); 207 190 } // if … … 231 214 } // distribution 232 215 233 forall( dtype T | { T *& Back ( T * ); T *& Next ( T * ); }) {216 forall( dtype T ) { 234 217 // SeqIter(T) is used to iterate over a Sequence(T) in head-to-tail order. 235 218 struct SeqIter { … … 241 224 }; 242 225 243 staticinline {226 inline { 244 227 void ?{}( SeqIter(T) & si ) with( si ) { 245 228 ((ColIter &)si){}; 246 229 seq = 0p; 247 } // post: elts = null 248 249 // Create a iterator active in sequence s. 230 } // post: elts = null. 231 250 232 void ?{}( SeqIter(T) & si, Sequence(T) & s ) with( si ) { 251 233 ((ColIter &)si){}; 252 234 seq = &s; 253 235 curr = &head( s ); 254 } // post: elts = null 236 } // post: elts = null. 255 237 256 238 void ?{}( SeqIter(T) & si, Sequence(T) & s, T & start ) with( si ) { … … 258 240 seq = &s; 259 241 curr = &start; 260 } // post: elts = null 261 262 // Make the iterator active in sequence s. 242 } // post: elts = null. 243 263 244 void over( SeqIter(T) & si, Sequence(T) & s ) with( si ) { 264 245 seq = &s; 265 246 curr = &head( s ); 266 } // post: elts = {e in s} 267 268 bool ? |?( SeqIter(T) & si, T && tp ) with( si ) {247 } // post: elts = {e in s}. 248 249 bool ?>>?( SeqIter(T) & si, T && tp ) with( si ) { 269 250 if ( curr ) { 270 251 &tp = Curr( si ); … … 284 265 }; 285 266 286 staticinline {267 inline { 287 268 void ?{}( SeqIterRev(T) & si ) with( si ) { 288 269 ((ColIter &)si){}; 289 270 seq = 0p; 290 } // post: elts = null 291 292 // Create a iterator active in sequence s. 271 } // post: elts = null. 272 293 273 void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 294 274 ((ColIter &)si){}; 295 275 seq = &s; 296 276 curr = &tail( s ); 297 } // post: elts = null 277 } // post: elts = null. 298 278 299 279 void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) { … … 301 281 seq = &s; 302 282 curr = &start; 303 } // post: elts = null 304 305 // Make the iterator active in sequence s. 283 } // post: elts = null. 284 306 285 void over( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 307 286 seq = &s; 308 287 curr = &tail( s ); 309 } // post: elts = {e in s} 310 311 bool ? |?( SeqIterRev(T) & si, T && tp ) with( si ) {288 } // post: elts = {e in s}. 289 290 bool ?>>?( SeqIterRev(T) & si, T && tp ) with( si ) { 312 291 if ( curr ) { 313 292 &tp = Curr( si ); … … 319 298 } // distribution 320 299 } // distribution 321 322 #endif -
libcfa/src/bits/stack.hfa
r2b4daf2 r42f6e07 3 3 #include "bits/collection.hfa" 4 4 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 * ); } ) { 5 forall( dtype T ) { 12 6 struct Stack { 13 7 inline Collection; // Plan 9 inheritance … … 31 25 } 32 26 33 T &addHead( Stack(T) & s, T & n ) with( s ) {27 void addHead( Stack(T) & s, T & n ) with( s ) { 34 28 #ifdef __CFA_DEBUG__ 35 29 if ( listed( (Colable &)(n) ) ) abort( "(Stack &)%p.addHead( %p ) : Node is already on another list.", &s, n ); … … 37 31 Next( &n ) = &head( s ) ? &head( s ) : &n; 38 32 root = &n; 39 return n;40 33 } 41 34 42 T &add( Stack(T) & s, T & n ) with( s ) {43 returnaddHead( s, n );35 void add( Stack(T) & s, T & n ) with( s ) { 36 addHead( s, n ); 44 37 } 45 38 46 T &push( Stack(T) & s, T & n ) with( s ) {47 returnaddHead( s, n );39 void push( Stack(T) & s, T & n ) with( s ) { 40 addHead( s, n ); 48 41 } 49 42 … … 51 44 T & t = head( s ); 52 45 if ( root ) { 53 root = ( T *)Next( (T *)root );46 root = ( T *)Next( root ); 54 47 if ( &head( s ) == &t ) root = 0p; // only one element ? 55 48 Next( &t ) = 0p; … … 64 57 } // distribution 65 58 66 // A StackIter(T) is a subclass of ColIter(T) that generates the elements of a Stack(T). It returns the elements in the67 // order returned by drop().68 59 69 forall( dtype T | { T *& Next ( T * ); }) {60 forall( dtype T ) { 70 61 struct StackIter { 71 62 inline ColIter; // Plan 9 inheritance … … 77 68 } // post: curr == 0p 78 69 79 // create an iterator active in stack s70 // create an iterator active in Stack s 80 71 void ?{}( StackIter(T) & si, Stack(T) & s ) with( si ) { 81 72 curr = &head( s ); … … 86 77 } // post: curr = {e in s} 87 78 88 // make existing iterator active in stack s79 // make existing iterator active in Stack q 89 80 void over( StackIter(T) & si, Stack(T) & s ) with( si ) { 90 81 curr = &head( s ); 91 82 } // post: curr = {e in s} 92 83 93 bool ? |?( StackIter(T) & si, T && tp ) with( si ) {84 bool ?>>?( StackIter(T) & si, T && tp ) with( si ) { 94 85 if ( curr ) { 95 86 &tp = Curr( si ); -
libcfa/src/concurrency/invoke.h
r2b4daf2 r42f6e07 189 189 struct __monitor_group_t monitors; 190 190 191 // used to put threads on user data structures192 struct {193 struct $thread * next;194 struct $thread * back;195 } seqable;196 197 191 struct { 198 192 struct $thread * next; … … 224 218 } 225 219 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 238 220 static inline void ?{}(__monitor_group_t & this) { 239 221 (this.data){0p}; -
libcfa/src/concurrency/kernel/startup.cfa
r2b4daf2 r42f6e07 118 118 119 119 extern size_t __page_size; 120 extern int __map_prot;121 120 122 121 //----------------------------------------------------------------------------- … … 726 725 } 727 726 #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 } // if733 );734 727 free( stack ); 735 728 #endif -
libcfa/src/concurrency/locks.cfa
r2b4daf2 r42f6e07 1 1 #include "locks.hfa" 2 2 #include "kernel_private.hfa" 3 #include <stdlib.h> 4 #include <stdio.h> 3 5 4 6 #include <kernel.hfa> 5 7 #include <stdlib.hfa> 6 7 //----------------------------------------------------------------------------- 8 // info_thread 8 #include <thread.hfa> 9 10 /////////////////////////////////////////////////////////////////// 11 //// info_thread 12 /////////////////////////////////////////////////////////////////// 13 9 14 forall(dtype L | is_blocking_lock(L)) { 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 ) { 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 ) { 28 23 ((Seqable &) this){}; 29 24 this.t = t; 30 25 this.info = info; 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 26 this.lock = 0p; 27 this.listed = false; 28 } 29 30 void ^?{}( info_thread(L) & this ){ } 31 } 32 33 /////////////////////////////////////////////////////////////////// 34 //// Blocking Locks 35 /////////////////////////////////////////////////////////////////// 36 47 37 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ) { 48 38 this.lock{}; … … 56 46 57 47 void ^?{}( blocking_lock & this ) {} 58 void 48 void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };} 59 49 void ^?{}( single_acquisition_lock & this ) {} 60 void 50 void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };} 61 51 void ^?{}( owner_lock & this ) {} 62 void 52 void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };} 63 53 void ^?{}( multiple_acquisition_lock & this ) {} 64 54 65 55 void lock( blocking_lock & this ) with( this ) { 66 56 lock( lock __cfaabi_dbg_ctx2 ); 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 ); 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() ); 75 61 wait_count++; 76 62 unlock( lock ); 77 63 park( ); 78 } 79 // multi acquisition lock is held by current thread 80 else if ( owner == thrd && multi_acquisition ) { 64 } else if ( owner == active_thread() && multi_acquisition ) { 81 65 recursion_count++; 82 66 unlock( lock ); 83 } 84 // lock isn't held 85 else { 86 owner = thrd; 67 } else { 68 owner = active_thread(); 87 69 recursion_count = 1; 88 70 unlock( lock ); … … 93 75 bool ret = false; 94 76 lock( lock __cfaabi_dbg_ctx2 ); 95 96 // lock isn't held97 77 if ( owner == 0p ) { 98 78 owner = active_thread(); 99 79 recursion_count = 1; 100 80 ret = true; 101 } 102 // multi acquisition lock is held by current thread 103 else if ( owner == active_thread() && multi_acquisition ) { 81 } else if ( owner == active_thread() && multi_acquisition ) { 104 82 recursion_count++; 105 83 ret = true; 106 84 } 107 108 85 unlock( lock ); 109 86 return ret; 110 87 } 111 88 89 void unlock_error_check( blocking_lock & this ) with( this ) { 90 if ( owner == 0p ){ // no owner implies lock isn't held 91 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 112 97 void pop_and_set_new_owner( blocking_lock & this ) with( this ) { 113 $thread * t = &dropHead( blocked_threads );98 $thread * t = pop_head( blocked_threads ); 114 99 owner = t; 115 100 recursion_count = ( t ? 1 : 0 ); … … 120 105 void unlock( blocking_lock & this ) with( this ) { 121 106 lock( lock __cfaabi_dbg_ctx2 ); 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 107 unlock_error_check( this ); 126 108 recursion_count--; 127 109 if ( recursion_count == 0 ) { … … 143 125 } 144 126 145 void on_notify( blocking_lock & this, $thread * t ) with( this ) { 146 lock( lock __cfaabi_dbg_ctx2 ); 147 // lock held 127 void add_( blocking_lock & this, $thread * t ) with( this ) { 128 lock( lock __cfaabi_dbg_ctx2 ); 148 129 if ( owner != 0p ) { 149 a ddTail( blocked_threads, *t );130 append( blocked_threads, t ); 150 131 wait_count++; 151 132 unlock( lock ); 152 } 153 // lock not held 154 else { 133 } else { 155 134 owner = t; 156 135 recursion_count = 1; … … 160 139 } 161 140 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 141 void remove_( blocking_lock & this ) with( this ) { 142 lock( lock __cfaabi_dbg_ctx2 ); 143 unlock_error_check( this ); 167 144 pop_and_set_new_owner( this ); 168 145 unlock( lock ); 169 146 } 170 147 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 ); } 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 ); } 193 173 size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); } 194 174 195 //----------------------------------------------------------------------------- 196 // alarm node wrapper 175 /////////////////////////////////////////////////////////////////// 176 //// condition variable 177 /////////////////////////////////////////////////////////////////// 178 197 179 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 ) { }211 180 212 181 void timeout_handler ( alarm_node_wrap(L) & this ) with( this ) { 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 ); 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) 223 188 cond->count--; 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 189 if( !i->lock ) { 229 190 unpark( i->t ); 230 } 231 } 232 unlock( cond->lock ); 233 } 234 235 // this casts the alarm node to our wrapped type since we used type erasure 191 } else { 192 add_(*i->lock, i->t); // call lock's add_ 193 } 194 } 195 unlock( cond->lock ); 196 } 197 236 198 void alarm_node_wrap_cast( alarm_node_t & a ) { timeout_handler( (alarm_node_wrap(L) &)a ); } 237 }238 239 //-----------------------------------------------------------------------------240 // condition variable241 forall(dtype L | is_blocking_lock(L)) {242 199 243 200 void ?{}( condition_variable(L) & this ){ … … 245 202 this.blocked_threads{}; 246 203 this.count = 0; 204 this.last_thread = 0p; // REMOVE AFTER DEBUG 247 205 } 248 206 249 207 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 ) { } 250 214 251 215 void process_popped( condition_variable(L) & this, info_thread(L) & popped ) with( this ) { 252 216 if(&popped != 0p) { 253 popped. signalled = true;217 popped.listed = false; 254 218 count--; 255 219 if (popped.lock) { 256 // if lock passed call on_notify 257 on_notify(*popped.lock, popped.t); 220 add_(*popped.lock, popped.t); 258 221 } else { 259 // otherwise wake thread260 222 unpark(popped.t); 261 223 } … … 290 252 291 253 size_t queue_and_get_recursion( condition_variable(L) & this, info_thread(L) * i ) with(this) { 292 // add info_thread to waiting queue293 254 addTail( blocked_threads, *i ); 294 255 count++; 256 i->listed = true; 295 257 size_t recursion_count = 0; 296 258 if (i->lock) { 297 // if lock was passed get recursion count to reset to after waking thread259 i->t->link.next = 1p; 298 260 recursion_count = get_recursion_count(*i->lock); 299 on_wait( *i->lock );261 remove_( *i->lock ); 300 262 } 301 263 return recursion_count; … … 307 269 size_t recursion_count = queue_and_get_recursion(this, &i); 308 270 unlock( lock ); 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 ); 271 park( ); // blocks here 272 if (i.lock) set_recursion_count(*i.lock, recursion_count); // resets recursion count here after waking 273 } 320 274 321 275 // helper for wait()'s' with a timeout … … 323 277 lock( lock __cfaabi_dbg_ctx2 ); 324 278 size_t recursion_count = queue_and_get_recursion(this, &info); 325 alarm_node_wrap(L) node_wrap = { t, 0`s, alarm_node_wrap_cast, &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; 326 282 register_self( &node_wrap.alarm_node ); 327 283 unlock( lock ); 328 329 // blocks here330 284 park(); 331 332 // unregisters alarm so it doesn't go off if this happens first333 285 unregister_self( &node_wrap.alarm_node ); 334 335 // resets recursion count here after waking336 286 if (info.lock) set_recursion_count(*info.lock, recursion_count); 337 287 } 338 288 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 } 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 } -
libcfa/src/concurrency/locks.hfa
r2b4daf2 r42f6e07 3 3 #include <stdbool.h> 4 4 5 #include "bits/algorithm.hfa" 5 6 #include "bits/locks.hfa" 6 7 #include "bits/sequence.hfa" 8 #include "bits/containers.hfa" 7 9 8 10 #include "invoke.h" … … 10 12 #include "time_t.hfa" 11 13 #include "time.hfa" 14 #include <sys/time.h> 15 #include "alarm.hfa" 12 16 13 //----------------------------------------------------------------------------- 14 // is_blocking_lock 17 /////////////////////////////////////////////////////////////////// 18 //// is_blocking_lock 19 /////////////////////////////////////////////////////////////////// 20 15 21 trait is_blocking_lock(dtype L | sized(L)) { 16 // For synchronization locks to use when acquiring 17 void on_notify( L &, struct $thread * ); 18 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 ); 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; 27 26 }; 28 27 29 // -----------------------------------------------------------------------------30 // info_thread31 // the info thread is a wrapper around a thread used32 // to store extra data for use in the condition variable 28 /////////////////////////////////////////////////////////////////// 29 //// info_thread 30 /////////////////////////////////////////////////////////////////// 31 33 32 forall(dtype L | is_blocking_lock(L)) { 34 struct info_thread; 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 }; 35 40 36 // for use by sequence 37 info_thread(L) *& Back( info_thread(L) * this ); 38 info_thread(L) *& Next( info_thread(L) * this ); 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 ); 39 45 } 40 46 41 //----------------------------------------------------------------------------- 42 // Blocking Locks 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 43 61 struct blocking_lock { 44 62 // Spin lock used for mutual exclusion … … 46 64 47 65 // List of blocked threads 48 Sequence( $thread ) blocked_threads;66 __queue_t( $thread ) blocked_threads; 49 67 50 68 // Count of current blocked threads … … 76 94 }; 77 95 78 void 96 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ); 79 97 void ^?{}( blocking_lock & this ); 80 98 81 void 99 void ?{}( single_acquisition_lock & this ); 82 100 void ^?{}( single_acquisition_lock & this ); 83 101 84 void 102 void ?{}( owner_lock & this ); 85 103 void ^?{}( owner_lock & this ); 86 104 87 void 105 void ?{}( multiple_acquisition_lock & this ); 88 106 void ^?{}( multiple_acquisition_lock & this ); 89 107 … … 91 109 bool try_lock( blocking_lock & this ); 92 110 void unlock( blocking_lock & this ); 93 void on_notify( blocking_lock & this, struct $thread * t );94 void on_wait( blocking_lock & this );111 void add_( blocking_lock & this, struct $thread * t ); 112 void remove_( blocking_lock & this ); 95 113 size_t wait_count( blocking_lock & this ); 96 114 void set_recursion_count( blocking_lock & this, size_t recursion ); … … 99 117 void lock( single_acquisition_lock & this ); 100 118 void unlock( single_acquisition_lock & this ); 101 void on_notify( single_acquisition_lock & this, struct $thread * t );102 void on_wait( single_acquisition_lock & this );119 void add_( single_acquisition_lock & this, struct $thread * t ); 120 void remove_( single_acquisition_lock & this ); 103 121 void set_recursion_count( single_acquisition_lock & this, size_t recursion ); 104 122 size_t get_recursion_count( single_acquisition_lock & this ); … … 106 124 void lock( owner_lock & this ); 107 125 void unlock( owner_lock & this ); 108 void on_notify( owner_lock & this, struct $thread * t );109 void on_wait( owner_lock & this );126 void add_( owner_lock & this, struct $thread * t ); 127 void remove_( owner_lock & this ); 110 128 void set_recursion_count( owner_lock & this, size_t recursion ); 111 129 size_t get_recursion_count( owner_lock & this ); … … 113 131 void lock( multiple_acquisition_lock & this ); 114 132 void unlock( multiple_acquisition_lock & this ); 115 void on_notify( multiple_acquisition_lock & this, struct $thread * t );116 void on_wait( multiple_acquisition_lock & this );133 void add_( multiple_acquisition_lock & this, struct $thread * t ); 134 void remove_( multiple_acquisition_lock & this ); 117 135 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ); 118 136 size_t get_recursion_count( multiple_acquisition_lock & this ); 119 137 120 //----------------------------------------------------------------------------- 121 // Synchronization Locks 138 /////////////////////////////////////////////////////////////////// 139 //// Synchronization Locks 140 /////////////////////////////////////////////////////////////////// 122 141 forall(dtype L | is_blocking_lock(L)) { 123 142 struct condition_variable { 124 143 // Spin lock used for mutual exclusion 125 144 __spinlock_t lock; 145 146 info_thread(L) * last_thread; 126 147 127 148 // List of blocked threads … … 132 153 }; 133 154 134 void 155 void ?{}( condition_variable(L) & this ); 135 156 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 ); 136 172 137 173 bool notify_one( condition_variable(L) & this ); … … 140 176 uintptr_t front( condition_variable(L) & this ); 141 177 142 bool empty 143 int 178 bool empty( condition_variable(L) & this ); 179 int counter( condition_variable(L) & this ); 144 180 181 // TODO: look into changing timout routines to return bool showing if signalled or woken by kernel 145 182 void wait( condition_variable(L) & this ); 146 183 void wait( condition_variable(L) & this, uintptr_t info ); 147 boolwait( condition_variable(L) & this, Duration duration );148 boolwait( condition_variable(L) & this, uintptr_t info, Duration duration );149 boolwait( condition_variable(L) & this, Time time );150 boolwait( condition_variable(L) & this, uintptr_t info, Time time );184 void wait( condition_variable(L) & this, Duration duration ); 185 void wait( condition_variable(L) & this, uintptr_t info, Duration duration ); 186 void wait( condition_variable(L) & this, Time time ); 187 void wait( condition_variable(L) & this, uintptr_t info, Time time ); 151 188 152 189 void wait( condition_variable(L) & this, L & l ); 153 190 void wait( condition_variable(L) & this, L & l, uintptr_t info ); 154 boolwait( condition_variable(L) & this, L & l, Duration duration );155 boolwait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration );156 boolwait( condition_variable(L) & this, L & l, Time time );157 boolwait( condition_variable(L) & this, L & l, uintptr_t info, Time time );191 void wait( condition_variable(L) & this, L & l, Duration duration ); 192 void wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ); 193 void wait( condition_variable(L) & this, L & l, Time time ); 194 void wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ); 158 195 } -
libcfa/src/concurrency/monitor.hfa
r2b4daf2 r42f6e07 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; }135 134 bool signal ( condition & this ); 136 135 bool signal_block( condition & this ); 137 static inline bool signal_all ( condition & this ) { bool ret = false; while(!is_empty(this)) { ret = signal(this) || ret; } return ret; }136 static inline bool is_empty ( condition & this ) { return this.blocked.head == 1p; } 138 137 uintptr_t front ( condition & this ); 139 138 -
libcfa/src/concurrency/thread.cfa
r2b4daf2 r42f6e07 43 43 canary = 0x0D15EA5E0D15EA5Ep; 44 44 #endif 45 46 seqable.next = 0p;47 seqable.back = 0p;48 45 49 46 node.next = 0p; … … 133 130 134 131 this_thrd->context.[SP, FP] = this_thrd->self_cor.context.[SP, FP]; 135 /* paranoid */verify( this_thrd->context.SP );132 verify( this_thrd->context.SP ); 136 133 137 134 __schedule_thread( this_thrd ); -
libcfa/src/heap.cfa
r2b4daf2 r42f6e07 10 10 // Created On : Tue Dec 19 21:58:35 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Dec 16 12:28:25202013 // Update Count : 10 2312 // Last Modified On : Tue Dec 15 21:37:54 2020 13 // Update Count : 1013 14 14 // 15 15 … … 438 438 header = headerAddr( addr ); 439 439 440 if ( unlikely( addr < heapBegin || heapEnd < addr ) ) {// mmapped ?440 if ( unlikely( 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( header < (HeapManager.Storage.Header *)heapBegin, name, addr );// bad low address ?447 checkHeader( addr < heapBegin, name, addr ); // bad low address ? 448 448 #endif // __CFA_DEBUG__ 449 449 … … 484 484 #endif // __CFA_DEBUG__ 485 485 486 487 486 #define NO_MEMORY_MSG "insufficient heap memory available for allocating %zd new bytes." 488 487 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, '\ xde', increase );513 memset( (char *)heapEnd + heapRemaining, '\hde', 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, '\ xde', tsize );588 memset( block, '\hde', 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, '\ xde', freeElem->blockSize - sizeof( HeapManager.Storage ) );636 memset( ((HeapManager.Storage *)header)->data, '\hde', 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 1032 1031 // Same as memalign(), but ISO/IEC 2011 C11 Section 7.22.2 states: the value of size shall be an integral multiple 1033 1032 // of alignment. This requirement is universally ignored. … … 1046 1045 return 0; 1047 1046 } // posix_memalign 1048 1049 1047 1050 1048 // Allocates size bytes and returns a pointer to the allocated memory. The memory address shall be a multiple of the -
libcfa/src/memory.cfa
r2b4daf2 r42f6e07 66 66 forall(dtype T | sized(T), ttype Args | { void ?{}(T&, Args); }) 67 67 void ?{}(counter_ptr(T) & this, Args args) { 68 this.data = (counter_data(T)*)new(args);68 this.data = 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 = (T *)new(args);128 this.data = new(args); 129 129 } 130 130 -
libcfa/src/parseargs.cfa
r2b4daf2 r42f6e07 105 105 if(opt == options[i].short_name) { 106 106 const char * arg = optarg ? optarg : ""; 107 if( arg[0] == '=' ) { arg++; }108 107 bool success = options[i].parse( arg, options[i].variable ); 109 108 if(success) continue NEXT_ARG; … … 186 185 } 187 186 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 202 187 bool parse_settrue (const char *, bool & value ) { 203 188 value = true; -
libcfa/src/parseargs.hfa
r2b4daf2 r42f6e07 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_truefalse(const char *, bool & ); 41 bool parse_settrue (const char *, bool & ); 42 bool parse_setfalse (const char *, bool & ); 39 bool parse_yesno (const char *, bool & ); 40 bool parse_settrue (const char *, bool & ); 41 bool parse_setfalse(const char *, bool & ); 43 42 44 43 bool parse(const char *, const char * & ); -
libcfa/src/stdlib.hfa
r2b4daf2 r42f6e07 267 267 static inline forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } ) 268 268 T * new( TT p ) { 269 return &(* (T *)malloc()){ p }; // run constructor269 return &(*malloc()){ p }; // run constructor 270 270 } // new 271 271 -
longrun_tests/Makefile.am
r2b4daf2 r42f6e07 44 44 -DTEST_$(shell cat .type | tr a-z A-Z) 45 45 46 TESTS = block coroutine create disjoint enter enter3 locksprocessor stack wait yield46 TESTS = block coroutine create disjoint enter enter3 processor stack wait yield 47 47 48 48 # .INTERMEDIATE: $(TESTS) -
src/AST/Convert.cpp
r2b4daf2 r42f6e07 205 205 ftype->parameters = get<DeclarationWithType>().acceptL(node->params); 206 206 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 } 207 ftype->forall = get<TypeDecl>().acceptL( node->type->forall ); 213 208 214 209 visitType(node->type, ftype); … … 607 602 608 603 for (decltype(src->begin()) src_i = src->begin(); src_i != src->end(); src_i++) { 609 rslt->add( src_i->first .typeString(),604 rslt->add( src_i->first, 610 605 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 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 1214 ty->forall = get<TypeDecl>().acceptL( node->forall ); 1235 1215 return visitType( node, ty ); 1236 1216 } … … 1319 1299 ty = new TypeInstType{ 1320 1300 cv( node ), 1321 node-> typeString(),1301 node->name, 1322 1302 get<TypeDecl>().accept1( node->base ), 1323 1303 get<Attribute>().acceptL( node->attributes ) … … 1326 1306 ty = new TypeInstType{ 1327 1307 cv( node ), 1328 node-> typeString(),1308 node->name, 1329 1309 node->kind == ast::TypeDecl::Ftype, 1330 1310 get<Attribute>().acceptL( node->attributes ) … … 1451 1431 /// at conversion stage, all created nodes are guaranteed to be unique, therefore 1452 1432 /// const_casting out of smart pointers is permitted. 1453 std::unordered_map< const BaseSyntaxNode *, ast:: readonly<ast::Node> > cache = {};1433 std::unordered_map< const BaseSyntaxNode *, ast::ptr<ast::Node> > cache = {}; 1454 1434 1455 1435 // Local Utilities: … … 1585 1565 // can function type have attributes? seems not to be the case. 1586 1566 // visitType(old->type, ftype); 1587 1588 // collect assertions and put directly in FunctionDecl1589 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 }1599 1567 1600 1568 auto decl = new ast::FunctionDecl{ … … 1616 1584 cache.emplace( old, decl ); 1617 1585 1618 decl->assertions = std::move(assertions);1619 1586 decl->withExprs = GET_ACCEPT_V(withExprs, Expr); 1620 1587 decl->stmts = GET_ACCEPT_1(statements, CompoundStmt); … … 2099 2066 } 2100 2067 2101 // TypeSubstitution shouldn't exist yet in old.2102 2068 ast::TypeSubstitution * convertTypeSubstitution(const TypeSubstitution * old) { 2103 2069 2104 2070 if (!old) return nullptr; 2105 if (old->empty()) return nullptr; 2106 assert(false); 2107 2108 /* 2071 2109 2072 ast::TypeSubstitution *rslt = new ast::TypeSubstitution(); 2110 2073 … … 2114 2077 } 2115 2078 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 2116 2084 return rslt; 2117 */2118 2085 } 2119 2086 … … 2643 2610 ty->params.emplace_back(v->get_type()); 2644 2611 } 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 } 2612 ty->forall = GET_ACCEPT_V( forall, TypeDecl ); 2655 2613 visitType( old, ty ); 2656 2614 } -
src/AST/Decl.cpp
r2b4daf2 r42f6e07 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 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 } 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 } 70 68 71 69 -
src/AST/Decl.hpp
r2b4daf2 r42f6e07 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;131 129 // declared type, derived from parameter declarations 132 130 ptr<FunctionType> type; 133 131 ptr<CompoundStmt> stmts; 134 132 std::vector< ptr<Expr> > withExprs; 135 136 133 137 134 FunctionDecl( const CodeLocation & loc, const std::string & name, std::vector<ptr<TypeDecl>>&& forall, -
src/AST/Expr.cpp
r2b4daf2 r42f6e07 206 206 assert( aggregate->result ); 207 207 208 result = mem->get_type(); 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()); 209 244 210 245 // substitute aggregate generic parameters into member type -
src/AST/Node.hpp
r2b4daf2 r42f6e07 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; } 232 operator const node_t * () && = delete; 231 operator const node_t * () const { _check(); return node; } 233 232 234 233 const node_t * release() { -
src/AST/Pass.hpp
r2b4daf2 r42f6e07 34 34 35 35 #include "AST/SymbolTable.hpp" 36 37 #include "AST/ForallSubstitutionTable.hpp" 36 38 37 39 // Private prelude header, needed for some of the magic tricks this class pulls off … … 64 66 // | WithVisitorRef - provides an pointer to the templated visitor wrapper 65 67 // | WithSymbolTable - provides symbol table functionality 68 // | WithForallSubstitutor - maintains links between TypeInstType and TypeDecl under mutation 66 69 // 67 70 // Other Special Members: … … 255 258 container_t< ptr<node_t> > call_accept( const container_t< ptr<node_t> > & container ); 256 259 260 /// Mutate forall-list, accounting for presence of type substitution map 261 template<typename node_t> 262 void mutate_forall( const node_t *& ); 263 257 264 public: 258 265 /// Logic to call the accept and mutate the parent if needed, delegates call to accept … … 391 398 }; 392 399 400 /// Use when the templated visitor needs to keep TypeInstType instances properly linked to TypeDecl 401 struct WithForallSubstitutor { 402 ForallSubstitutionTable subs; 403 }; 404 393 405 } 394 406 -
src/AST/Pass.impl.hpp
r2b4daf2 r42f6e07 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 clone 375 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 mutate 382 maybe_accept( node, &node_t::forall ); 383 } 384 } 369 385 } 370 386 … … 488 504 __pass::symtab::addId( core, 0, func ); 489 505 VISIT( 490 // parameter declarations 506 // parameter declarations are now directly here 491 507 maybe_accept( node, &FunctionDecl::params ); 492 508 maybe_accept( node, &FunctionDecl::returns ); 493 // type params and assertions 494 maybe_accept( node, &FunctionDecl::type_params ); 495 maybe_accept( node, &FunctionDecl::assertions ); 509 // foralls are still in function type 510 maybe_accept( node, &FunctionDecl::type ); 496 511 // First remember that we are now within a function. 497 512 ValueGuard< bool > oldInFunction( inFunction ); … … 1743 1758 1744 1759 VISIT({ 1745 // guard_forall_subs forall_guard { *this, node }; 1746 // mutate_forall( node ); 1747 maybe_accept( node, &FunctionType::assertions ); 1760 guard_forall_subs forall_guard { *this, node }; 1761 mutate_forall( node ); 1748 1762 maybe_accept( node, &FunctionType::returns ); 1749 1763 maybe_accept( node, &FunctionType::params ); … … 1967 1981 { 1968 1982 bool mutated = false; 1969 std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map;1983 std::unordered_map< std::string, ast::ptr< ast::Type > > new_map; 1970 1984 for ( const auto & p : node->typeEnv ) { 1971 1985 guard_symtab guard { *this }; … … 1980 1994 } 1981 1995 } 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 } 1982 2012 ) 1983 2013 -
src/AST/Pass.proto.hpp
r2b4daf2 r42f6e07 413 413 static inline auto leave( core_t &, long, const ast::FunctionType * ) {} 414 414 415 // Get the substitution table, if present 416 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 415 424 // Replaces a TypeInstType's base TypeDecl according to the table 416 425 template<typename core_t> -
src/AST/Print.cpp
r2b4daf2 r42f6e07 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 166 157 void print( const std::vector<ptr<Attribute>> & attrs ) { 167 158 if ( attrs.empty() ) return; … … 215 206 void preprint( const ast::NamedTypeDecl * node ) { 216 207 if ( ! node->name.empty() ) { 217 os << node->name << ": "; 208 if( deterministic_output && isUnboundType(node->name) ) os << "[unbound]:"; 209 else os << node->name << ": "; 218 210 } 219 211 … … 269 261 void preprint( const ast::FunctionType * node ) { 270 262 print( node->forall ); 271 print( node->assertions );272 263 print( node->qualifiers ); 273 264 } … … 1384 1375 virtual const ast::Type * visit( const ast::TypeInstType * node ) override final { 1385 1376 preprint( node ); 1386 const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node-> typeString();1377 const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->name; 1387 1378 os << "instance of type " << _name 1388 1379 << " (" << (node->kind == ast::TypeDecl::Ftype ? "" : "not ") << "function type)"; … … 1511 1502 os << indent << "Types:" << endl; 1512 1503 for ( const auto& i : *node ) { 1513 os << indent+1 << i.first .typeString()<< " -> ";1504 os << indent+1 << i.first << " -> "; 1514 1505 indent += 2; 1515 1506 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 ); 1516 1515 indent -= 2; 1517 1516 os << endl; -
src/AST/SymbolTable.cpp
r2b4daf2 r42f6e07 414 414 415 415 void SymbolTable::addFunction( const FunctionDecl * func ) { 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 ); 416 addTypes( func->type->forall ); 423 417 addIds( func->returns ); 424 418 addIds( func->params ); -
src/AST/Type.cpp
r2b4daf2 r42f6e07 21 21 22 22 #include "Decl.hpp" 23 #include "ForallSubstitutor.hpp" // for substituteForall 23 24 #include "Init.hpp" 24 25 #include "Common/utility.h" // for copy, move … … 91 92 // GENERATED END 92 93 94 // --- ParameterizedType 95 96 void FunctionType::initWithSub( 97 const FunctionType & o, Pass< ForallSubstitutor > & sub 98 ) { 99 forall = sub.core( o.forall ); 100 } 101 93 102 // --- 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 map 110 returns = sub.core( o.returns ); // apply to return and parameter types 111 params = sub.core( o.params ); 112 } 113 94 114 namespace { 95 115 bool containsTtype( const std::vector<ptr<Type>> & l ) { … … 179 199 // TODO: once TypeInstType representation is updated, it should properly check 180 200 // if the context id is filled. this is a temporary hack for now 181 return typeInst->formal_usage > 0; 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; 182 213 } 183 214 return false; -
src/AST/Type.hpp
r2b4daf2 r42f6e07 36 36 37 37 template< typename T > class Pass; 38 39 struct ForallSubstitutor; 38 40 39 41 class Type : public Node { … … 270 272 /// Type of a function `[R1, R2](*)(P1, P2, P3)` 271 273 class FunctionType final : public Type { 272 public: 273 using ForallList = std::vector<ptr<TypeInstType>>; 274 using AssertionList = std::vector<ptr<VariableExpr>>; 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>>; 275 279 ForallList forall; 276 AssertionList assertions;277 280 278 281 std::vector<ptr<Type>> returns; … … 289 292 : Type(q), returns(), params(), isVarArgs(va) {} 290 293 291 FunctionType( const FunctionType & o ) = default;294 FunctionType( const FunctionType & o ); 292 295 293 296 /// true if either the parameters or return values contain a tttype … … 394 397 public: 395 398 readonly<TypeDecl> base; 396 // previously from renameTyVars; now directly use integer fields instead of synthesized strings397 // 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; }418 400 419 401 TypeInstType( … … 427 409 TypeInstType( const TypeInstType & o ) = default; 428 410 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 432 411 /// sets `base`, updating `kind` correctly 433 412 void set_base( const TypeDecl * ); … … 439 418 440 419 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 }446 420 private: 447 421 TypeInstType * clone() const override { return new TypeInstType{ *this }; } … … 536 510 537 511 bool isUnboundType(const Type * type); 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 }; 512 bool isUnboundType(const std::string & tname); 513 552 514 } 553 515 -
src/AST/TypeEnvironment.cpp
r2b4daf2 r42f6e07 52 52 for ( const auto & i : open ) { 53 53 if ( first ) { first = false; } else { out << ' '; } 54 out << i.first .typeString()<< "(" << i.second << ")";54 out << i.first << "(" << i.second << ")"; 55 55 } 56 56 } … … 62 62 if(first) first = false; 63 63 else out << " "; 64 65 if( deterministic_output ) out << "[unbound]"; 66 else out << "_" << var.formal_usage << "_" << var.expr_id << "_"; 67 68 out << var.base->name; 64 if( deterministic_output && isUnboundType(var) ) out << "[unbound]"; 65 else out << var; 69 66 } 70 67 out << ")"; … … 82 79 } 83 80 84 const EqvClass * TypeEnvironment::lookup( const TypeInstType::TypeEnvKey& var ) const {81 const EqvClass * TypeEnvironment::lookup( const std::string & var ) const { 85 82 for ( ClassList::const_iterator i = env.begin(); i != env.end(); ++i ) { 86 83 if ( i->vars.find( var ) != i->vars.end() ) return &*i; … … 109 106 110 107 void TypeEnvironment::add( const FunctionType::ForallList & tyDecls ) { 111 for ( auto &tyDecl : tyDecls ) {108 for ( const TypeDecl * tyDecl : tyDecls ) { 112 109 env.emplace_back( tyDecl ); 113 110 } … … 122 119 void TypeEnvironment::writeToSubstitution( TypeSubstitution & sub ) const { 123 120 for ( const auto & clz : env ) { 124 TypeInstType::TypeEnvKey clzRep; 125 bool first = true; 121 std::string clzRep; 126 122 for ( const auto & var : clz.vars ) { 127 123 if ( clz.bound ) { 128 124 sub.add( var, clz.bound ); 129 } else if ( first) {125 } else if ( clzRep.empty() ) { 130 126 clzRep = var; 131 first = false;132 127 } else { 133 sub.add( var, new TypeInstType{ clzRep } );128 sub.add( var, new TypeInstType{ clzRep, clz.data.kind } ); 134 129 } 135 130 } … … 146 141 struct Occurs : public ast::WithVisitorRef<Occurs> { 147 142 bool result; 148 std:: unordered_set< TypeInstType::TypeEnvKey> vars;143 std::set< std::string > vars; 149 144 const TypeEnvironment & tenv; 150 145 151 Occurs( const TypeInstType::TypeEnvKey& var, const TypeEnvironment & env )146 Occurs( const std::string & var, const TypeEnvironment & env ) 152 147 : result( false ), vars(), tenv( env ) { 153 148 if ( const EqvClass * clz = tenv.lookup( var ) ) { … … 159 154 160 155 void previsit( const TypeInstType * typeInst ) { 161 if ( vars.count( *typeInst) ) {156 if ( vars.count( typeInst->name ) ) { 162 157 result = true; 163 } else if ( const EqvClass * clz = tenv.lookup( *typeInst) ) {158 } else if ( const EqvClass * clz = tenv.lookup( typeInst->name ) ) { 164 159 if ( clz->bound ) { 165 160 clz->bound->accept( *visitor ); … … 170 165 171 166 /// true if `var` occurs in `ty` under `env` 172 bool occurs( const Type * ty, const TypeInstType::TypeEnvKey& var, const TypeEnvironment & env ) {167 bool occurs( const Type * ty, const std::string & var, const TypeEnvironment & env ) { 173 168 Pass<Occurs> occur{ var, env }; 174 169 maybe_accept( ty, occur ); … … 285 280 // remove references from bound type, so that type variables can only bind to value types 286 281 ptr<Type> target = bindTo->stripReferences(); 287 auto tyvar = open.find( *typeInst);282 auto tyvar = open.find( typeInst->name ); 288 283 assert( tyvar != open.end() ); 289 284 if ( ! tyVarCompatible( tyvar->second, target ) ) return false; 290 if ( occurs( target, *typeInst, *this ) ) return false;291 292 auto it = internal_lookup( *typeInst);285 if ( occurs( target, typeInst->name, *this ) ) return false; 286 287 auto it = internal_lookup( typeInst->name ); 293 288 if ( it != env.end() ) { 294 289 if ( it->bound ) { … … 313 308 } else { 314 309 env.emplace_back( 315 *typeInst, target, widen.first && widen.second, data );310 typeInst->name, target, widen.first && widen.second, data ); 316 311 } 317 312 return true; … … 323 318 WidenMode widen, const SymbolTable & symtab 324 319 ) { 325 auto c1 = internal_lookup( *var1);326 auto c2 = internal_lookup( *var2);320 auto c1 = internal_lookup( var1->name ); 321 auto c2 = internal_lookup( var2->name ); 327 322 328 323 // exit early if variables already bound together … … 338 333 if ( c1 != env.end() ) { 339 334 if ( c1->bound ) { 340 if ( occurs( c1->bound, *var2, *this ) ) return false;335 if ( occurs( c1->bound, var2->name, *this ) ) return false; 341 336 type1 = c1->bound; 342 337 } … … 345 340 if ( c2 != env.end() ) { 346 341 if ( c2->bound ) { 347 if ( occurs( c2->bound, *var1, *this ) ) return false;342 if ( occurs( c2->bound, var1->name, *this ) ) return false; 348 343 type2 = c2->bound; 349 344 } … … 383 378 } else if ( c1 != env.end() ) { 384 379 // var2 unbound, add to env[c1] 385 c1->vars.emplace( *var2);380 c1->vars.emplace( var2->name ); 386 381 c1->allowWidening = widen1; 387 382 c1->data.isComplete |= data.isComplete; 388 383 } else if ( c2 != env.end() ) { 389 384 // var1 unbound, add to env[c2] 390 c2->vars.emplace( *var1);385 c2->vars.emplace( var1->name ); 391 386 c2->allowWidening = widen2; 392 387 c2->data.isComplete |= data.isComplete; 393 388 } else { 394 389 // neither var bound, create new class 395 env.emplace_back( *var1, *var2, widen1 && widen2, data );390 env.emplace_back( var1->name, var2->name, widen1 && widen2, data ); 396 391 } 397 392 … … 457 452 } 458 453 459 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const TypeInstType::TypeEnvKey& var ) {454 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const std::string & var ) { 460 455 for ( ClassList::iterator i = env.begin(); i != env.end(); ++i ) { 461 456 if ( i->vars.count( var ) ) return i; -
src/AST/TypeEnvironment.hpp
r2b4daf2 r42f6e07 55 55 /// recorded. More investigation is needed. 56 56 struct AssertCompare { 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);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() ); 60 60 } 61 61 }; … … 70 70 71 71 /// Set of assertions pending satisfaction 72 using AssertionSet = std::map< const VariableExpr *, AssertionSetValue, AssertCompare >;72 using AssertionSet = std::map< readonly<DeclWithType>, AssertionSetValue, AssertCompare >; 73 73 74 74 /// Set of open variables 75 using OpenVarSet = std::unordered_map< TypeInstType::TypeEnvKey, TypeDecl::Data >;75 using OpenVarSet = std::unordered_map< std::string, 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:: unordered_set< TypeInstType::TypeEnvKey> vars;91 std::set< std::string > vars; 92 92 ptr<Type> bound; 93 93 bool allowWidening; … … 101 101 102 102 /// Singleton class constructor from TypeDecl 103 EqvClass( const Type InstType * inst)104 : vars{ *inst }, bound(), allowWidening( true ), data( inst->base) {}103 EqvClass( const TypeDecl * decl ) 104 : vars{ decl->name }, bound(), allowWidening( true ), data( decl ) {} 105 105 106 106 /// Singleton class constructor from substitution 107 EqvClass( const TypeInstType::TypeEnvKey& v, const Type * b )107 EqvClass( const std::string & 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 TypeInstType::TypeEnvKey& v, const Type * b, bool w, const TypeDecl::Data & d )111 EqvClass( const std::string & 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 TypeInstType::TypeEnvKey & v, const TypeInstType::TypeEnvKey& u, bool w, const TypeDecl::Data & d )117 EqvClass( const std::string & v, const std::string & 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 TypeInstType::TypeEnvKey& var ) const;133 const EqvClass * lookup( const std::string & 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 TypeInstType::TypeEnvKey& );209 ClassList::iterator internal_lookup( const std::string & ); 210 210 }; 211 211 -
src/AST/TypeSubstitution.cpp
r2b4daf2 r42f6e07 39 39 void TypeSubstitution::initialize( const TypeSubstitution &src, TypeSubstitution &dest ) { 40 40 dest.typeEnv.clear(); 41 dest.varEnv.clear(); 41 42 dest.add( src ); 42 43 } … … 46 47 typeEnv[ i->first ] = i->second; 47 48 } // for 48 } 49 50 void TypeSubstitution::add( const TypeInstType * formalType, const Type *actualType ) { 51 typeEnv[ *formalType ] = actualType; 52 } 53 54 void TypeSubstitution::add( const TypeInstType::TypeEnvKey & key, const Type * actualType) { 55 typeEnv[ key ] = actualType; 56 } 57 58 void TypeSubstitution::remove( const TypeInstType * formalType ) { 59 TypeEnvType::iterator i = typeEnv.find( *formalType ); 49 for ( VarEnvType::const_iterator i = other.varEnv.begin(); i != other.varEnv.end(); ++i ) { 50 varEnv[ i->first ] = i->second; 51 } // for 52 } 53 54 void TypeSubstitution::add( std::string formalType, const Type *actualType ) { 55 typeEnv[ formalType ] = actualType; 56 } 57 58 void TypeSubstitution::addVar( std::string formalExpr, const Expr *actualExpr ) { 59 varEnv[ formalExpr ] = actualExpr; 60 } 61 62 void TypeSubstitution::remove( std::string formalType ) { 63 TypeEnvType::iterator i = typeEnv.find( formalType ); 60 64 if ( i != typeEnv.end() ) { 61 typeEnv.erase( *formalType );62 } // if 63 } 64 65 const Type *TypeSubstitution::lookup( const TypeInstType *formalType ) const {66 TypeEnvType::const_iterator i = typeEnv.find( *formalType );65 typeEnv.erase( formalType ); 66 } // if 67 } 68 69 const Type *TypeSubstitution::lookup( std::string formalType ) const { 70 TypeEnvType::const_iterator i = typeEnv.find( formalType ); 67 71 68 72 // break on not in substitution set … … 71 75 // attempt to transitively follow TypeInstType links. 72 76 while ( const TypeInstType *actualType = i->second.as<TypeInstType>()) { 77 const std::string& typeName = actualType->name; 78 73 79 // break cycles in the transitive follow 74 if ( *formalType == *actualType ) break;80 if ( formalType == typeName ) break; 75 81 76 82 // Look for the type this maps to, returning previous mapping if none-such 77 i = typeEnv.find( *actualType );83 i = typeEnv.find( typeName ); 78 84 if ( i == typeEnv.end() ) return actualType; 79 85 } … … 84 90 85 91 bool TypeSubstitution::empty() const { 86 return typeEnv.empty() ;92 return typeEnv.empty() && varEnv.empty(); 87 93 } 88 94 … … 92 98 TypeSubstitution * newEnv; 93 99 EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){} 94 void previsit( FunctionType * ftype) {100 void previsit( TypeDecl * tyDecl ) { 95 101 // transfer known bindings for seen type variables 96 for (auto & formal : ftype->forall) { 97 if ( const Type * t = env->lookup( formal ) ) { 98 newEnv->add( formal, t ); 99 } 102 if ( const Type * t = env->lookup( tyDecl->name ) ) { 103 newEnv->add( tyDecl->name, t ); 100 104 } 101 105 } … … 126 130 127 131 const Type * TypeSubstitution::Substituter::postvisit( const TypeInstType *inst ) { 128 BoundVarsType::const_iterator bound = boundVars.find( *inst);132 BoundVarsType::const_iterator bound = boundVars.find( inst->name ); 129 133 if ( bound != boundVars.end() ) return inst; 130 134 131 TypeEnvType::const_iterator i = sub.typeEnv.find( *inst);135 TypeEnvType::const_iterator i = sub.typeEnv.find( inst->name ); 132 136 if ( i == sub.typeEnv.end() ) { 133 137 return inst; … … 137 141 // TODO: investigate preventing type variables from being bound to themselves in the first place. 138 142 if ( const TypeInstType * replacement = i->second.as<TypeInstType>() ) { 139 if ( *inst == *replacement) {143 if ( inst->name == replacement->name ) { 140 144 return inst; 141 145 } … … 152 156 } 153 157 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 } // if 166 } 167 154 168 void TypeSubstitution::Substituter::previsit( const FunctionType * ptype ) { 155 169 GuardValue( boundVars ); 156 170 // bind type variables from forall-qualifiers 157 171 if ( freeOnly ) { 158 for ( auto &tyvar : ptype->forall ) {159 boundVars.insert( *tyvar);172 for ( const TypeDecl * tyvar : ptype->forall ) { 173 boundVars.insert( tyvar->name ); 160 174 } // for 161 175 } // if 162 176 } 163 177 164 /*165 178 void TypeSubstitution::Substituter::handleAggregateType( const BaseInstType * type ) { 166 179 GuardValue( boundVars ); … … 171 184 if ( ! type->params.empty() ) { 172 185 for ( const TypeDecl * tyvar : decl->params ) { 173 boundVars.insert( *tyvar);186 boundVars.insert( tyvar->name ); 174 187 } // for 175 188 } // if … … 185 198 handleAggregateType( aggregateUseType ); 186 199 } 187 */188 200 189 201 } // namespace ast -
src/AST/TypeSubstitution.hpp
r2b4daf2 r42f6e07 69 69 } 70 70 71 void add( const TypeInstType * formalType, const Type *actualType ); 72 void add( const TypeInstType::TypeEnvKey & key, const Type *actualType ); 71 void add( std::string formalType, const Type *actualType ); 73 72 void add( const TypeSubstitution &other ); 74 void remove( const TypeInstType *formalType );75 const Type *lookup( const TypeInstType *formalType ) const;73 void remove( std::string formalType ); 74 const Type *lookup( std::string formalType ) const; 76 75 bool empty() const; 76 77 void addVar( std::string formalExpr, const Expr *actualExpr ); 77 78 78 79 template< typename FormalIterator, typename ActualIterator > … … 100 101 friend class Pass; 101 102 102 typedef std::unordered_map< TypeInstType::TypeEnvKey, ptr<Type> > TypeEnvType; 103 typedef std::unordered_map< std::string, ptr<Type> > TypeEnvType; 104 typedef std::unordered_map< std::string, ptr<Expr> > VarEnvType; 103 105 TypeEnvType typeEnv; 106 VarEnvType varEnv; 104 107 105 108 public: … … 110 113 auto end() const -> decltype( typeEnv. end() ) { return typeEnv. end(); } 111 114 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(); } 112 119 }; 113 120 114 // this is the only place where type parameters outside a function formal may be substituted.115 121 template< typename FormalIterator, typename ActualIterator > 116 122 void TypeSubstitution::add( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) { … … 123 129 if ( const TypeExpr *actual = actualIt->template as<TypeExpr>() ) { 124 130 if ( formal->name != "" ) { 125 typeEnv[ formal ] = actual->type;131 typeEnv[ formal->name ] = actual->type; 126 132 } // if 127 133 } else { … … 129 135 } // if 130 136 } else { 131 137 // TODO: type check the formal and actual parameters 138 if ( (*formalIt)->name != "" ) { 139 varEnv[ (*formalIt)->name ] = *actualIt; 140 } // if 132 141 } // if 133 142 } // for 134 143 } 135 136 137 144 138 145 template< typename FormalIterator, typename ActualIterator > … … 140 147 add( formalBegin, formalEnd, actualBegin ); 141 148 } 142 143 149 144 150 } // namespace ast … … 158 164 159 165 const Type * postvisit( const TypeInstType * aggregateUseType ); 166 const Expr * postvisit( const NameExpr * nameExpr ); 160 167 161 168 /// Records type variable bindings from forall-statements 162 169 void previsit( const FunctionType * type ); 163 170 /// Records type variable bindings from forall-statements and instantiations of generic types 164 //void handleAggregateType( const BaseInstType * type );165 166 //void previsit( const StructInstType * aggregateUseType );167 //void previsit( const UnionInstType * aggregateUseType );171 void handleAggregateType( const BaseInstType * type ); 172 173 void previsit( const StructInstType * aggregateUseType ); 174 void previsit( const UnionInstType * aggregateUseType ); 168 175 169 176 const TypeSubstitution & sub; 170 177 int subCount = 0; 171 178 bool freeOnly; 172 typedef std::unordered_set< TypeInstType::TypeEnvKey> BoundVarsType;179 typedef std::unordered_set< std::string > BoundVarsType; 173 180 BoundVarsType boundVars; 174 181 -
src/AST/module.mk
r2b4daf2 r42f6e07 33 33 AST/Expr.cpp \ 34 34 AST/Expr.hpp \ 35 AST/ForallSubstitutionTable.cpp \ 36 AST/ForallSubstitutionTable.hpp \ 37 AST/ForallSubstitutor.hpp \ 35 38 AST/FunctionSpec.hpp \ 36 39 AST/Fwd.hpp \ -
src/GenPoly/GenPoly.cc
r2b4daf2 r42f6e07 115 115 if (!env) return type; 116 116 if (auto typeInst = dynamic_cast<const ast::TypeInstType*> (type)) { 117 auto newType = env->lookup(typeInst );117 auto newType = env->lookup(typeInst->name); 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-> typeString()) != tyVars.end() ? type : nullptr;174 return tyVars.find(typeInst->name) != 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 InstType* tyVar, TyVarMap & tyVarMap) {555 tyVarMap.insert(tyVar-> typeString(), convData(ast::TypeDecl::Data{tyVar->base}));554 void addToTyVarMap( const ast::TypeDecl * tyVar, TyVarMap & tyVarMap) { 555 tyVarMap.insert(tyVar->name, convData(ast::TypeDecl::Data{tyVar})); 556 556 } 557 557 -
src/InitTweak/GenInit.cc
r2b4daf2 r42f6e07 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 single126 /// const variable of type size_t, so that side effecting array dimensions are only127 /// 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 objects134 // that need to be constructed or destructed135 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 145 124 void genInit( std::list< Declaration * > & translationUnit ) { 146 if (!useNewAST) { 147 HoistArrayDimension::hoistArrayDimension( translationUnit ); 148 } 149 else { 150 HoistArrayDimension_NoResolve::hoistArrayDimension( translationUnit ); 151 } 125 HoistArrayDimension::hoistArrayDimension( translationUnit ); 152 126 fixReturnStatements( translationUnit ); 153 127 … … 222 196 223 197 // need to resolve array dimensions in order to accurately determine if constexpr 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 ); 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 } 227 203 // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects. 228 204 // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve … … 242 218 243 219 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 var267 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 resolve277 // still try to detect constant expressions278 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 * ) {292 220 GuardValue( inFunction ); 293 221 inFunction = true; -
src/ResolvExpr/AdjustExprType.cc
r2b4daf2 r42f6e07 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) ) {135 if ( const ast::EqvClass * eqvClass = tenv.lookup( inst->name ) ) { 136 136 if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) { 137 137 return new ast::PointerType{ inst }; -
src/ResolvExpr/CandidateFinder.cpp
r2b4daf2 r42f6e07 212 212 // mark type variable and specialization cost of forall clause 213 213 convCost.incVar( function->forall.size() ); 214 convCost.decSpec( function->assertions.size() ); 214 for ( const ast::TypeDecl * td : function->forall ) { 215 convCost.decSpec( td->assertions.size() ); 216 } 215 217 216 218 return convCost; … … 221 223 ast::AssertionSet & need 222 224 ) { 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;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 } 228 230 } 229 231 } … … 905 907 // xxx - is it possible that handleTupleAssignment and main finder both produce candidates? 906 908 // this means there exists ctor/assign functions with a tuple as first parameter. 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 ); 909 funcFinder.find( untypedExpr->func, selfFinder.candidates.empty() ? ResolvMode::withAdjustment() : ResolvMode::withoutFailFast() ); 913 910 // short-circuit if no candidates 914 911 // if ( funcFinder.candidates.empty() ) return; … … 956 953 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult ) 957 954 ) { 958 if ( const ast::EqvClass * clz = func->env.lookup( *inst) ) {955 if ( const ast::EqvClass * clz = func->env.lookup( inst->name ) ) { 959 956 if ( auto function = clz->bound.as< ast::FunctionType >() ) { 960 957 CandidateRef newFunc{ new Candidate{ *func } }; … … 1080 1077 assert( toType ); 1081 1078 toType = resolveTypeof( toType, symtab ); 1082 //toType = SymTab::validateType( castExpr->location, toType, symtab );1079 toType = SymTab::validateType( castExpr->location, toType, symtab ); 1083 1080 toType = adjustExprType( toType, tenv, symtab ); 1084 1081 … … 1165 1162 1166 1163 if(auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) { 1167 auto td = cand->env.lookup( *insttype);1164 auto td = cand->env.lookup(insttype->name); 1168 1165 if(!td) { continue; } 1169 1166 expr = td->bound.get(); … … 1571 1568 // calculate target type 1572 1569 const ast::Type * toType = resolveTypeof( initAlt.type, symtab ); 1573 //toType = SymTab::validateType( initExpr->location, toType, symtab );1570 toType = SymTab::validateType( initExpr->location, toType, symtab ); 1574 1571 toType = adjustExprType( toType, tenv, symtab ); 1575 1572 // The call to find must occur inside this loop, otherwise polymorphic return -
src/ResolvExpr/CastCost.cc
r2b4daf2 r42f6e07 202 202 ) { 203 203 if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 204 if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst) ) {204 if ( const ast::EqvClass * eqvClass = env.lookup( typeInst->name ) ) { 205 205 // check cast cost against bound type, if present 206 206 if ( eqvClass->bound ) { -
src/ResolvExpr/CommonType.cc
r2b4daf2 r42f6e07 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);715 auto entry = open.find( var->name ); 716 716 if ( entry != open.end() ) { 717 717 ast::AssertionSet need, have; -
src/ResolvExpr/ConversionCost.cc
r2b4daf2 r42f6e07 498 498 ) { 499 499 if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 500 if ( const ast::EqvClass * eqv = env.lookup( *inst) ) {500 if ( const ast::EqvClass * eqv = env.lookup( inst->name ) ) { 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 ) ) {677 if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) { 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 == *dstAsInst) {681 if ( typeInstType->name == dstAsInst->name ) { 682 682 cost = Cost::zero; 683 683 } -
src/ResolvExpr/FindOpenVars.cc
r2b4daf2 r42f6e07 112 112 // mark open/closed variables 113 113 if ( nextIsOpen ) { 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;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 } 119 119 } 120 120 } else { 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;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 } 126 126 } 127 127 } -
src/ResolvExpr/PolyCost.cc
r2b4daf2 r42f6e07 68 68 69 69 void previsit( const ast::TypeInstType * type ) { 70 if ( const ast::EqvClass * eqv = env_.lookup( *type ) ) /* && */ if ( eqv->bound ) {70 if ( const ast::EqvClass * eqv = env_.lookup( type->name ) ) /* && */ 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
r2b4daf2 r42f6e07 134 134 } 135 135 void postvisit( const ast::TypeInstType * inst ) { 136 if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst) ) {136 if ( const ast::EqvClass * eqv = typeEnv.lookup( inst->name ) ) { 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) ) {148 if ( const ast::EqvClass * eqv = env.lookup( dstAsInst->name ) ) { 149 149 return ptrsAssignable( src, eqv->bound, env ); 150 150 } -
src/ResolvExpr/PtrsCastable.cc
r2b4daf2 r42f6e07 180 180 } 181 181 } 182 } else if ( const ast::EqvClass * eqvClass = env.lookup( *inst) ) {182 } else if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) { 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) ) {285 if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) { 286 286 return ptrsAssignable( src, eqvClass->bound, env ); 287 287 } -
src/ResolvExpr/RenameVars.cc
r2b4daf2 r42f6e07 19 19 #include <utility> // for pair 20 20 21 #include "AST/ForallSubstitutionTable.hpp" 21 22 #include "AST/Pass.hpp" 22 23 #include "AST/Type.hpp" … … 38 39 int level = 0; 39 40 int resetCount = 0; 41 ScopedMap< std::string, std::string > nameMap; 42 public: 43 ast::ForallSubstitutionTable subs; 40 44 41 int next_expr_id = 1;42 int next_usage_id = 1;43 ScopedMap< std::string, std::string > nameMap;44 ScopedMap< std::string, ast::TypeInstType::TypeEnvKey > idMap;45 public:46 45 void reset() { 47 46 level = 0; … … 54 53 type->name = it->second; 55 54 } 56 }57 58 void nextUsage() {59 ++next_usage_id;60 55 } 61 56 … … 83 78 84 79 const ast::TypeInstType * rename( const ast::TypeInstType * type ) { 80 // re-linking of base type handled by WithForallSubstitutor 81 85 82 // rename 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; 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; 94 89 type = mut; 95 90 } … … 98 93 } 99 94 100 const ast::FunctionType * openLevel( const ast::FunctionType * type , RenameMode mode) {95 const ast::FunctionType * openLevel( const ast::FunctionType * type ) { 101 96 if ( type->forall.empty() ) return type; 102 idMap.beginScope(); 97 98 nameMap.beginScope(); 103 99 104 100 // Load new names from this forall clause and perform renaming. 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" ); 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 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; 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 126 115 } 116 // assertion above means `type = mutType;` is unnecessary 127 117 128 return mutType;118 return type; 129 119 } 130 120 131 121 void closeLevel( const ast::FunctionType * type ) { 132 122 if ( type->forall.empty() ) return; 133 idMap.endScope(); 123 124 nameMap.endScope(); 134 125 } 135 126 }; … … 151 142 }; 152 143 153 struct RenameVars_new : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ { 154 RenameMode mode; 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; 155 147 156 148 const ast::FunctionType * previsit( const ast::FunctionType * type ) { 157 return renaming.openLevel( type , mode);149 return renaming.openLevel( type ); 158 150 } 159 151 … … 171 163 172 164 const ast::TypeInstType * previsit( const ast::TypeInstType * type ) { 173 if (mode == GEN_USAGE && !type->formal_usage) return type; // do not rename an actual type174 165 return renaming.rename( type ); 175 166 } … … 186 177 } 187 178 188 const ast::Type * renameTyVars( const ast::Type * t , RenameMode mode, bool reset) {189 //ast::Type *tc = ast::deepCopy(t);179 const ast::Type * renameTyVars( const ast::Type * t ) { 180 ast::Type *tc = ast::deepCopy(t); 190 181 ast::Pass<RenameVars_new> renamer; 191 renamer.core.mode = mode; 192 if (mode == GEN_USAGE && reset) { 193 renaming.nextUsage(); 194 } 195 return t->accept( renamer ); 182 // return t->accept( renamer ); 183 return tc->accept( renamer ); 196 184 } 197 185 198 186 void resetTyVarRenaming() { 199 187 renaming.reset(); 200 renaming.nextUsage();201 188 } 202 189 -
src/ResolvExpr/RenameVars.h
r2b4daf2 r42f6e07 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 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 32 const ast::Type * renameTyVars( const ast::Type * ); 39 33 40 34 /// resets internal state of renamer to avoid overflow 41 35 void resetTyVarRenaming(); 42 43 44 36 } // namespace ResolvExpr 45 37 -
src/ResolvExpr/ResolvMode.h
r2b4daf2 r42f6e07 23 23 const bool failFast; ///< Fail on no resulting alternatives? [true] 24 24 25 private: 25 26 constexpr ResolvMode(bool a, bool p, bool ff) 26 27 : adjust(a), prune(p), failFast(ff) {} 27 28 29 public: 28 30 /// Default settings 29 31 constexpr ResolvMode() : adjust(false), prune(true), failFast(true) {} -
src/ResolvExpr/ResolveAssertions.cc
r2b4daf2 r42f6e07 397 397 398 398 /// Limit to depth of recursion of assertion satisfaction 399 static const int recursionLimit = 7;399 static const int recursionLimit = 4; 400 400 /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of 401 401 static const int deferLimit = 10; -
src/ResolvExpr/ResolveTypeof.cc
r2b4daf2 r42f6e07 15 15 16 16 #include "ResolveTypeof.h" 17 #include "RenameVars.h"18 17 19 18 #include <cassert> // for assert … … 219 218 mutDecl->mangleName = Mangle::mangle(mutDecl); // do not mangle unnamed variables 220 219 221 mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);222 220 mutDecl->isTypeFixed = true; 223 221 return mutDecl; -
src/ResolvExpr/Resolver.cc
r2b4daf2 r42f6e07 986 986 }; 987 987 } // anonymous namespace 988 988 989 /// Check if this expression is or includes a deleted expression 989 990 const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) { … … 1151 1152 const ast::Expr * untyped, const ast::SymbolTable & symtab 1152 1153 ) { 1154 resetTyVarRenaming(); 1153 1155 ast::TypeEnvironment env; 1154 1156 ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, symtab, env ); … … 1310 1312 } 1311 1313 1312 const ast::Expr *resolveStmtExpr(1314 ast::ptr< ast::Expr > resolveStmtExpr( 1313 1315 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab 1314 1316 ) { 1315 1317 assert( stmtExpr ); 1316 1318 ast::Pass< Resolver_new > resolver{ symtab }; 1317 auto ret = mutate(stmtExpr->accept(resolver)); 1318 strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult(); 1319 ast::ptr< ast::Expr > ret = stmtExpr; 1320 ret = ret->accept( resolver ); 1321 strict_dynamic_cast< ast::StmtExpr * >( ret.get_and_mutate() )->computeResult(); 1319 1322 return ret; 1320 1323 } … … 1372 1375 } 1373 1376 1374 // handle assertions 1377 // handle assertions. (seems deep) 1375 1378 1376 1379 symtab.enterScope(); 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)); 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; 1387 1388 } 1388 1389 … … 1406 1407 mutType->returns = std::move(returnTypes); 1407 1408 1408 auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));1409 1410 1409 std::list<ast::ptr<ast::Stmt>> newStmts; 1411 1410 resolveWithExprs (mutDecl->withExprs, newStmts); … … 1419 1418 symtab.leaveScope(); 1420 1419 1421 mutDecl->type = renamedType;1422 1420 mutDecl->mangleName = Mangle::mangle(mutDecl); 1423 1421 mutDecl->isTypeFixed = true; … … 1536 1534 const PtrType * handlePtrType( const PtrType * type, const ast::SymbolTable & symtab ) { 1537 1535 if ( type->dimension ) { 1538 ast::ptr< ast::Type > sizeType = ast::sizeType; 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 }; 1539 1538 ast::mutate_field( 1540 1539 type, &PtrType::dimension, -
src/ResolvExpr/Resolver.h
r2b4daf2 r42f6e07 72 72 ast::ptr< ast::Init > resolveCtorInit( 73 73 const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab ); 74 /// Resolves a statement expression 75 const ast::Expr *resolveStmtExpr(74 /// Resolves a statement expression 75 ast::ptr< ast::Expr > resolveStmtExpr( 76 76 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab ); 77 77 } // namespace ResolvExpr -
src/ResolvExpr/SatisfyAssertions.cpp
r2b4daf2 r42f6e07 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:: VariableExpr * expr;71 const ast::DeclWithType * decl; 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:: VariableExpr * expr;79 const ast::DeclWithType * decl; 80 80 const ast::AssertionSetValue & info; 81 81 AssnCandidateList matches; 82 82 83 DeferItem( 84 const ast:: VariableExpr* d, const ast::AssertionSetValue & i, AssnCandidateList && ms )85 : expr( d ), info( i ), matches( std::move( ms ) ) {}83 DeferItem( 84 const ast::DeclWithType * d, const ast::AssertionSetValue & i, AssnCandidateList && ms ) 85 : decl( 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 { expr, info, matches[i] }; }91 DeferRef operator[] ( unsigned i ) const { return { decl, 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 ->var); }140 if ( i.second.isUsed ) { symtab.addId( i.first ); } 141 141 } 142 142 } 143 143 144 144 /// Binds a single assertion, updating satisfaction state 145 void bindAssertion( 146 const ast:: VariableExpr * expr, const ast::AssertionSetValue & info, CandidateRef & cand,145 void bindAssertion( 146 const ast::DeclWithType * decl, 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 ][ expr->var->uniqueId ] = ast::ParamEntry{159 candidate->uniqueId, candidate, match.adjType, expr->result, varExpr };158 inferred[ info.resnSlot ][ decl->uniqueId ] = ast::ParamEntry{ 159 candidate->uniqueId, candidate, match.adjType, decl->get_type(), varExpr }; 160 160 } 161 161 … … 169 169 170 170 std::vector<ast::SymbolTable::IdData> candidates; 171 auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first-> var->name);171 auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->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 = assn.first->result.strict_as<ast::PointerType>()->base174 ast::ptr<ast::Type> thisArgType = strict_dynamic_cast<const ast::PointerType *>(assn.first->get_type())->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-> var->name);186 candidates = sat.symtab.lookupId(assn.first->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-> result;203 ast::ptr< ast::Type > adjType = 204 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) , GEN_USAGE, false);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 ) ); 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. expr->result, false, symtab, env );339 assn.match.adjType, assn.decl->get_type(), false, symtab, env ); 340 340 341 341 // mark vars+specialization on function-type assertions … … 350 350 cost.incVar( func->forall.size() ); 351 351 352 cost.decSpec( func->assertions.size() ); 352 for ( const ast::TypeDecl * td : func->forall ) { 353 cost.decSpec( td->assertions.size() ); 354 } 353 355 } 354 356 } … … 385 387 386 388 /// Limit to depth of recursion of assertion satisfaction 387 static const int recursionLimit = 8;389 static const int recursionLimit = 4; 388 390 /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of 389 391 static const int deferLimit = 10; 390 392 } // anonymous namespace 391 393 392 void satisfyAssertions( 393 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 394 void satisfyAssertions( 395 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 394 396 std::vector<std::string> & errors 395 397 ) { … … 417 419 if ( it != thresholds.end() && it->second < sat.costs ) goto nextSat; 418 420 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()) { 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 ) ) { 435 425 Indenter tabs{ 3 }; 436 426 std::ostringstream ss; … … 438 428 print( ss, *sat.cand, ++tabs ); 439 429 ss << (tabs-1) << "Could not satisfy assertion:\n"; 440 ast::print( ss, next[0].first, tabs );430 ast::print( ss, assn.first, tabs ); 441 431 442 432 errors.emplace_back( ss.str() ); 443 433 goto nextSat; 444 434 } 445 sat.need = std::move(next);446 435 } 447 436 … … 449 438 // either add successful match or push back next state 450 439 if ( sat.newNeed.empty() ) { 451 finalizeAssertions( 440 finalizeAssertions( 452 441 sat.cand, sat.inferred, thresholds, std::move( sat.costs ), out ); 453 442 } else { … … 462 451 ss << (tabs-1) << "Too many non-unique satisfying assignments for assertions:\n"; 463 452 for ( const auto & d : sat.deferred ) { 464 ast::print( ss, d. expr, tabs );453 ast::print( ss, d.decl, tabs ); 465 454 } 466 455 … … 471 460 std::vector< CandidateEnvMerger::OutType > compatible = filterCombos( 472 461 sat.deferred, CandidateEnvMerger{ sat.cand->env, sat.cand->open, sat.symtab } ); 473 462 474 463 // fail early if no mutually-compatible assertion satisfaction 475 464 if ( compatible.empty() ) { … … 480 469 ss << (tabs-1) << "No mutually-compatible satisfaction for assertions:\n"; 481 470 for ( const auto& d : sat.deferred ) { 482 ast::print( ss, d. expr, tabs );471 ast::print( ss, d.decl, tabs ); 483 472 } 484 473 … … 494 483 // set up next satisfaction state 495 484 CandidateRef nextCand = std::make_shared<Candidate>( 496 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 485 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 497 486 ast::AssertionSet{} /* need moved into satisfaction state */, 498 487 sat.cand->cost, sat.cand->cvtCost ); … … 500 489 ast::AssertionSet nextNewNeed{ sat.newNeed }; 501 490 InferCache nextInferred{ sat.inferred }; 502 491 503 492 CostVec nextCosts{ sat.costs }; 504 493 nextCosts.back() += compat.cost; 505 494 506 495 ast::SymbolTable nextSymtab{ sat.symtab }; 507 496 … … 512 501 nextNewNeed.insert( match.need.begin(), match.need.end() ); 513 502 514 bindAssertion( r. expr, r.info, nextCand, match, nextInferred );503 bindAssertion( r.decl, r.info, nextCand, match, nextInferred ); 515 504 } 516 505 517 506 // either add successful match or push back next state 518 507 if ( nextNewNeed.empty() ) { 519 finalizeAssertions( 508 finalizeAssertions( 520 509 nextCand, nextInferred, thresholds, std::move( nextCosts ), out ); 521 510 } else { 522 nextSats.emplace_back( 523 std::move( nextCand ), std::move( nextNewNeed ), 524 std::move( nextInferred ), std::move( nextCosts ), 511 nextSats.emplace_back( 512 std::move( nextCand ), std::move( nextNewNeed ), 513 std::move( nextInferred ), std::move( nextCosts ), 525 514 std::move( nextSymtab ) ); 526 515 } … … 534 523 nextSats.clear(); 535 524 } 536 525 537 526 // exceeded recursion limit if reaches here 538 527 if ( out.empty() ) { -
src/ResolvExpr/Unify.cc
r2b4daf2 r42f6e07 773 773 774 774 const ast::Type * postvisit( const ast::TypeInstType * typeInst ) { 775 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst) ) {775 if ( const ast::EqvClass * clz = tenv.lookup( typeInst->name ) ) { 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 const ast::Type *tupleFromTypes( Iter crnt, Iter end ) {813 static ast::ptr< 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:: VariableExpr* assn ) {890 static void markAssertionSet( ast::AssertionSet & assns, const ast::DeclWithType * assn ) { 891 891 auto i = assns.find( assn ); 892 892 if ( i != assns.end() ) { … … 900 900 const ast::FunctionType * type 901 901 ) { 902 for ( auto & assert : type->assertions ) { 903 markAssertionSet( assn1, assert ); 904 markAssertionSet( assn2, assert ); 902 for ( const auto & tyvar : type->forall ) { 903 for ( const ast::DeclWithType * assert : tyvar->assertions ) { 904 markAssertionSet( assn1, assert ); 905 markAssertionSet( assn2, assert ); 906 } 905 907 } 906 908 } … … 1028 1030 1029 1031 void postvisit( const ast::TypeInstType * typeInst ) { 1030 assert( open.find( *typeInst) == open.end() );1032 assert( open.find( typeInst->name ) == open.end() ); 1031 1033 handleRefType( typeInst, type2 ); 1032 1034 } … … 1034 1036 private: 1035 1037 /// Creates a tuple type based on a list of Type 1036 static const ast::Type *tupleFromTypes(1038 static ast::ptr< ast::Type > tupleFromTypes( 1037 1039 const std::vector< ast::ptr< ast::Type > > & tys 1038 1040 ) { … … 1169 1171 auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 ); 1170 1172 ast::OpenVarSet::const_iterator 1171 entry1 = var1 ? open.find( *var1) : open.end(),1172 entry2 = var2 ? open.find( *var2) : open.end();1173 entry1 = var1 ? open.find( var1->name ) : open.end(), 1174 entry2 = var2 ? open.find( var2->name ) : open.end(); 1173 1175 bool isopen1 = entry1 != open.end(); 1174 1176 bool isopen2 = entry2 != open.end(); -
src/SymTab/Mangler.cc
r2b4daf2 r42f6e07 671 671 int dcount = 0, fcount = 0, vcount = 0, acount = 0; 672 672 mangleName += Encoding::forall; 673 for ( auto &decl : ptype->forall ) {673 for ( const ast::TypeDecl * 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 } // for689 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++;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 } // for 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
r2b4daf2 r42f6e07 271 271 }; 272 272 273 struct InitializerLength{273 struct ArrayLength : public WithIndexer { 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 289 284 void previsit( ArrayType * arrayType ); 290 285 }; … … 387 382 FixObjectType::fix, translationUnit); 388 383 } 389 Stats::Time::TimeCall("Initializer Length", 390 InitializerLength::computeLength, translationUnit); 391 if (!useNewAST) { 392 Stats::Time::TimeCall("Array Length", 393 ArrayLength::computeLength, translationUnit); 394 } 384 Stats::Time::TimeCall("Array Length", 385 ArrayLength::computeLength, translationUnit); 395 386 Stats::Time::TimeCall("Find Special Declarations", 396 387 Validate::findSpecialDecls, translationUnit); … … 1341 1332 } 1342 1333 1343 void InitializerLength::computeLength( std::list< Declaration * > & translationUnit ) {1344 PassVisitor<InitializerLength> len;1345 acceptAll( translationUnit, len );1346 }1347 1348 1334 void ArrayLength::computeLength( std::list< Declaration * > & translationUnit ) { 1349 1335 PassVisitor<ArrayLength> len; … … 1351 1337 } 1352 1338 1353 void InitializerLength::previsit( ObjectDecl * objDecl ) {1339 void ArrayLength::previsit( ObjectDecl * objDecl ) { 1354 1340 if ( ArrayType * at = dynamic_cast< ArrayType * >( objDecl->type ) ) { 1355 1341 if ( at->dimension ) return; … … 1361 1347 1362 1348 void ArrayLength::previsit( ArrayType * type ) { 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 ); 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 } 1371 1359 } 1372 1360 } … … 1474 1462 } 1475 1463 } 1476 1477 /*1478 1464 1479 1465 /// Associates forward declarations of aggregates with their definitions … … 1858 1844 } 1859 1845 }; 1860 */1861 1846 } // anonymous namespace 1862 1847 1863 /*1864 1848 const ast::Type * validateType( 1865 1849 const CodeLocation & loc, const ast::Type * type, const ast::SymbolTable & symtab ) { … … 1870 1854 return type->accept( lrt )->accept( fpd ); 1871 1855 } 1872 */1873 1856 1874 1857 } // namespace SymTab -
src/Tuples/TupleAssignment.cc
r2b4daf2 r42f6e07 504 504 505 505 std::vector< ast::ptr< ast::Expr > > match() override { 506 static UniqueName lhsNamer( "__massassign_L" ); 507 static UniqueName rhsNamer( "__massassign_R" ); 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" ); 508 509 // empty tuple case falls into this matcher 509 510 assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 ); … … 534 535 535 536 std::vector< ast::ptr< ast::Expr > > match() override { 536 static UniqueName lhsNamer( "__multassign_L" ); 537 static UniqueName rhsNamer( "__multassign_R" ); 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" ); 538 540 539 541 if ( lhs.size() != rhs.size() ) return {}; -
tests/.expect/KRfunctions.nast.x86.txt
r2b4daf2 r42f6e07 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 int )10)]{89 __attribute__ ((unused)) signed int (*_X11_retval_f12PA0A0i_1)[][((unsigned int )10)];88 signed int (*_X3f12FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned long int )10)]{ 89 __attribute__ ((unused)) signed int (*_X11_retval_f12PA0A0i_1)[][((unsigned long int )10)]; 90 90 } 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)];91 signed int (*_X3f13FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned long int )10)]{ 92 __attribute__ ((unused)) signed int (*_X11_retval_f13PA0A0i_1)[][((unsigned long int )10)]; 93 93 } 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)];94 signed int (*_X3f14FPA0A0i_iPiPi__1(signed int _X1ai_1, signed int *_X1bPi_1, signed int *_X1cPi_1))[][((unsigned long int )10)]{ 95 __attribute__ ((unused)) signed int (*_X11_retval_f14PA0A0i_1)[][((unsigned long 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
r2b4daf2 r42f6e07 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 int )5)];626 __attribute__ ((used,used,unused,used)) const signed int _X3vd6A0Ki_1[((unsigned int )5)];625 __attribute__ ((used,used,used)) const signed int _X3vd5A0Ki_1[((unsigned long int )5)]; 626 __attribute__ ((used,used,unused,used)) const signed int _X3vd6A0Ki_1[((unsigned long 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 int )5)];650 __attribute__ ((unused,unused,unused,unused,unused)) signed int **_X2t4A0PPi_2[((unsigned int )5)];649 __attribute__ ((unused,unused,unused)) signed int _X2t3A0i_2[((unsigned long int )5)]; 650 __attribute__ ((unused,unused,unused,unused,unused)) signed int **_X2t4A0PPi_2[((unsigned long 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 int )5)]));673 signed int _X4tpr4Fi_Fi_Pi___1(__attribute__ ((unused,unused)) signed int (*__anonymous_object1)(signed int __param_0[((unsigned long 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 int )5)];682 __attribute__ ((unused,unused,unused,unused,unused)) signed int (*_X3ad4PA0i_2)[((unsigned int )10)];681 __attribute__ ((unused,unused,unused)) signed int _X3ad3A0i_2[((unsigned long int )5)]; 682 __attribute__ ((unused,unused,unused,unused,unused)) signed int (*_X3ad4PA0i_2)[((unsigned long 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
r2b4daf2 r42f6e07 46 46 __attribute__ ((unused)) signed int (*_X11_retval_f10PA0i_1)[]; 47 47 } 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)];48 signed int (*_X3f11FPA0A0i___1())[][((unsigned long int )3)]{ 49 __attribute__ ((unused)) signed int (*_X11_retval_f11PA0A0i_1)[][((unsigned long int )3)]; 50 } 51 signed int (*_X3f12FPA0A0i___1())[][((unsigned long int )3)]{ 52 __attribute__ ((unused)) signed int (*_X11_retval_f12PA0A0i_1)[][((unsigned long 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 int )10)])[][((unsignedint )3)];253 signed int (*(*_X1pPA0A0PA0A0i_2)[][((unsigned int )10)])[][((unsignedint )3)];252 signed int (*(*_X2pcPA0A0PA0A0i_2)[][((unsigned long int )10)])[][((unsigned long int )3)]; 253 signed int (*(*_X1pPA0A0PA0A0i_2)[][((unsigned long int )10)])[][((unsigned long int )3)]; 254 254 signed int (*(*_X1pPA0Fi_i__2)[])(signed int __param_0); 255 255 } -
tests/Makefile.am
r2b4daf2 r42f6e07 103 103 104 104 mostlyclean-local : 105 find ${builddir} -not -path './__pycache__/*' -path '*.o' -delete106 find ${builddir} -not -path './__pycache__/*' -path '*/.err/*.log' -delete107 find ${builddir} -not -path './__pycache__/*' -path '*/.out/*.log' -delete108 105 rm -f ${EXTRA_PROGRAMS} 109 106 rm -rf __pycache__ 107 find ${builddir} -path '*.o' -delete 108 find ${builddir} -path '*/.err/*.log' -delete 109 find ${builddir} -path '*/.out/*.log' -delete 110 110 111 111 distclean-local : -
tests/concurrent/futures/.expect/basic.txt
r2b4daf2 r42f6e07 1 start2 1 done -
tests/concurrent/futures/basic.cfa
r2b4daf2 r42f6e07 1 1 #include <thread.hfa> 2 3 2 enum {NFUTURES = 10}; 4 3 … … 84 83 85 84 int main() { 86 printf( "start\n" ); // non-empty .expect file87 85 processor procs[2]; 88 86 { -
tests/errors/.expect/completeType.nast.x64.txt
r2b4daf2 r42f6e07 12 12 Application of 13 13 Variable Expression: *?: forall 14 instance of type DT (not function type)14 DT: data type 15 15 function 16 16 ... with parameters … … 21 21 ... with resolved type: 22 22 pointer to forall 23 instance of type [unbound] (not function type)23 [unbound]:data type 24 24 function 25 25 ... with parameters … … 41 41 void 42 42 ) 43 Environment:([unbound] DT) -> instance of struct A without body (no widening)43 Environment:([unbound]) -> instance of struct A without body (no widening) 44 44 45 45 … … 47 47 Application of 48 48 Variable Expression: *?: forall 49 instance of type DT (not function type)49 DT: data type 50 50 function 51 51 ... with parameters … … 56 56 ... with resolved type: 57 57 pointer to forall 58 instance of type [unbound] (not function type)58 [unbound]:data type 59 59 function 60 60 ... with parameters … … 76 76 void 77 77 ) 78 Environment:([unbound] DT) -> instance of struct B with body (no widening)78 Environment:([unbound]) -> 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 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 115 T: sized data type 116 ... with assertions 117 ?=?: pointer to function 126 118 ... with parameters 127 119 reference to instance of type T (not function type) … … 130 122 instance of type T (not function type) 131 123 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 124 ?{}: pointer to function 139 125 ... with parameters 140 126 reference to instance of type T (not function type) 141 127 ... returning nothing 142 128 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 129 ?{}: pointer to function 151 130 ... with parameters 152 131 reference to instance of type T (not function type) … … 154 133 ... returning nothing 155 134 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 135 ^?{}: pointer to function 163 136 ... with parameters 164 137 reference to instance of type T (not function type) 165 138 ... returning nothing 139 166 140 167 141 function … … 172 146 ... with resolved type: 173 147 pointer to forall 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 148 [unbound]:sized data type 149 ... with assertions 150 ?=?: pointer to function 185 151 ... with parameters 186 152 reference to instance of type [unbound] (not function type) … … 189 155 instance of type [unbound] (not function type) 190 156 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 157 ?{}: pointer to function 198 158 ... with parameters 199 159 reference to instance of type [unbound] (not function type) 200 160 ... returning nothing 201 161 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 162 ?{}: pointer to function 210 163 ... with parameters 211 164 reference to instance of type [unbound] (not function type) … … 213 166 ... returning nothing 214 167 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 168 ^?{}: pointer to function 222 169 ... with parameters 223 170 reference to instance of type [unbound] (not function type) 224 171 ... returning nothing 172 225 173 226 174 function … … 240 188 void 241 189 ) 242 Environment:([unbound] T) -> instance of type T (not function type) (no widening)190 Environment:([unbound]) -> instance of type T (not function type) (no widening) 243 191 244 192 Could not satisfy assertion: 245 Variable Expression:?=?: pointer to function193 ?=?: pointer to function 246 194 ... with parameters 247 reference to instance of type T(not function type)248 instance of type T(not function type)195 reference to instance of type [unbound] (not function type) 196 instance of type [unbound] (not function type) 249 197 ... returning 250 instance of type T(not function type)198 instance of type [unbound] (not function type) 251 199 252 ... with resolved type:253 pointer to function254 ... with parameters255 reference to instance of type [unbound] (not function type)256 instance of type [unbound] (not function type)257 ... returning258 instance of type [unbound] (not function type)259 -
tests/errors/.expect/completeType.nast.x86.txt
r2b4daf2 r42f6e07 12 12 Application of 13 13 Variable Expression: *?: forall 14 instance of type DT (not function type)14 DT: data type 15 15 function 16 16 ... with parameters … … 21 21 ... with resolved type: 22 22 pointer to forall 23 instance of type [unbound] (not function type)23 [unbound]:data type 24 24 function 25 25 ... with parameters … … 41 41 void 42 42 ) 43 Environment:([unbound] DT) -> instance of struct A without body (no widening)43 Environment:([unbound]) -> instance of struct A without body (no widening) 44 44 45 45 … … 47 47 Application of 48 48 Variable Expression: *?: forall 49 instance of type DT (not function type)49 DT: data type 50 50 function 51 51 ... with parameters … … 56 56 ... with resolved type: 57 57 pointer to forall 58 instance of type [unbound] (not function type)58 [unbound]:data type 59 59 function 60 60 ... with parameters … … 76 76 void 77 77 ) 78 Environment:([unbound] DT) -> instance of struct B with body (no widening)78 Environment:([unbound]) -> 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 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 115 T: sized data type 116 ... with assertions 117 ?=?: pointer to function 126 118 ... with parameters 127 119 reference to instance of type T (not function type) … … 130 122 instance of type T (not function type) 131 123 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 124 ?{}: pointer to function 139 125 ... with parameters 140 126 reference to instance of type T (not function type) 141 127 ... returning nothing 142 128 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 129 ?{}: pointer to function 151 130 ... with parameters 152 131 reference to instance of type T (not function type) … … 154 133 ... returning nothing 155 134 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 135 ^?{}: pointer to function 163 136 ... with parameters 164 137 reference to instance of type T (not function type) 165 138 ... returning nothing 139 166 140 167 141 function … … 172 146 ... with resolved type: 173 147 pointer to forall 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 148 [unbound]:sized data type 149 ... with assertions 150 ?=?: pointer to function 185 151 ... with parameters 186 152 reference to instance of type [unbound] (not function type) … … 189 155 instance of type [unbound] (not function type) 190 156 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 157 ?{}: pointer to function 198 158 ... with parameters 199 159 reference to instance of type [unbound] (not function type) 200 160 ... returning nothing 201 161 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 162 ?{}: pointer to function 210 163 ... with parameters 211 164 reference to instance of type [unbound] (not function type) … … 213 166 ... returning nothing 214 167 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 168 ^?{}: pointer to function 222 169 ... with parameters 223 170 reference to instance of type [unbound] (not function type) 224 171 ... returning nothing 172 225 173 226 174 function … … 240 188 void 241 189 ) 242 Environment:([unbound] T) -> instance of type T (not function type) (no widening)190 Environment:([unbound]) -> instance of type T (not function type) (no widening) 243 191 244 192 Could not satisfy assertion: 245 Variable Expression:?=?: pointer to function193 ?=?: pointer to function 246 194 ... with parameters 247 reference to instance of type T(not function type)248 instance of type T(not function type)195 reference to instance of type [unbound] (not function type) 196 instance of type [unbound] (not function type) 249 197 ... returning 250 instance of type T(not function type)198 instance of type [unbound] (not function type) 251 199 252 ... with resolved type:253 pointer to function254 ... with parameters255 reference to instance of type [unbound] (not function type)256 instance of type [unbound] (not function type)257 ... returning258 instance of type [unbound] (not function type)259 -
tests/raii/.expect/ctor-autogen-ERR1.nast.txt
r2b4daf2 r42f6e07 70 70 ... with environment: 71 71 Types: 72 Non-types: 72 73 73 74
Note: See TracChangeset
for help on using the changeset viewer.