Changes in / [42f6e07:2b4daf2]


Ignore:
Files:
57 added
11 deleted
87 edited

Legend:

Unmodified
Added
Removed
  • Jenkins/FullBuild

    r42f6e07 r2b4daf2  
    3232
    3333                        stage('Package') {
    34                                 build job: 'Cforall_Distribute_Ref', parameters: [string(name: 'GitRef', value: commitId), string(name: 'Build', value: currentBuild.number.toString())]
     34                                trigger_dist( commitId, currentBuild.number.toString() )
    3535                        }
    3636                }
     
    103103}
    104104
     105def trigger_dist(String commitId, String buildNum) {
     106        def result = build job: 'Cforall_Distribute_Ref',       \
     107                parameters: [                                           \
     108                        string(name: 'GitRef', value: commitId),        \
     109                        string(name: 'Build' , value: buildNum) \
     110                ],                                                              \
     111                propagate: false
     112
     113        echo(result.result)
     114
     115        if(result.result != 'SUCCESS') {
     116                sh("wget -q -O - https://cforall.uwaterloo.ca/jenkins/job/Cforall_Distribute_Ref/${result.number}/consoleText")
     117                error(result.result)
     118        }
     119}
     120
    105121//===========================================================================================================
    106122//Routine responsible of sending the email notification once the build is completed
  • Jenkins/tools.groovy

    r42f6e07 r2b4daf2  
    66import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
    77
    8 // Global for the stage name
    9 StageName = ''
    10 
    118// wrapper around stage declaretion to be more verbose
    129// and allow showing as skipped in the UI
    1310def BuildStage(String name, boolean run, Closure block ) {
    14         StageName = name
    15         echo " -------- ${StageName} -------- "
     11        echo " -------- ${name} -------- "
    1612        if(run) {
    1713                stage(name, block)
  • Jenkinsfile

    r42f6e07 r2b4daf2  
    5454        //attach the build log to the email
    5555        catch (Exception caughtError) {
    56                 //rethrow error later
     56                // Store the result of the build log
     57                currentBuild.result = "FAILURE"
     58
     59                // An error has occured, the build log is relevent
     60                log_needed = true
     61
     62                // rethrow error later
    5763                err = caughtError
    5864
     65                // print the error so it shows in the log
    5966                echo err.toString()
    60 
    61                 //An error has occured, the build log is relevent
    62                 log_needed = true
    63 
    64                 //Store the result of the build log
    65                 currentBuild.result = "${tools.StageName} FAILURE".trim()
    6667        }
    6768
  • benchmark/Makefile.am

    r42f6e07 r2b4daf2  
    522522size-cfa$(EXEEXT):
    523523        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/size/size.cfa
     524
     525## =========================================================================================================
     526
     527%-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs
     528        cd $(builddir) && cargo build --release
     529        cp $(builddir)/target/release/$(basename $@) $@
  • benchmark/creation/node_cor.js

    r42f6e07 r2b4daf2  
    66function * coroutine() { yield }
    77
    8 for ( var i = 0; i < times; i += 1 ) { // warm jit
     8for ( var i = 0; i < times; i += 1 ) { // warm JIT
    99        cor = coroutine()
    1010}
  • benchmark/ctxswitch/node_cor.js

    r42f6e07 r2b4daf2  
    1111cor = coroutine()
    1212
    13 for ( var i = 0; i < times; i += 1 ) { // warm git
     13for ( var i = 0; i < times; i += 1 ) { // warm JIT
    1414        cor.next();
    1515}
  • benchmark/readyQ/bench.go

    r42f6e07 r2b4daf2  
    55        "flag"
    66        "fmt"
     7        "log"
    78        "os"
    89        "runtime"
     10        "runtime/pprof"
    911        "sync/atomic"
    1012        "time"
     
    4345}
    4446
    45 func bench_init() {
     47func bench_init() func() {
    4648        nprocsOpt := flag.Int("p", 1, "The number of processors")
    4749        nthreadsOpt := flag.Int("t", 1, "The number of threads")
    4850        durationOpt := flag.Float64("d", 0, "Duration of the experiment in seconds")
    4951        stopOpt := flag.Uint64("i", 0, "Duration of the experiment in iterations")
     52        cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file")
    5053
    5154        flag.Parse()
     
    7275
    7376        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        }
    7491}
  • benchmark/readyQ/cycle.cpp

    r42f6e07 r2b4daf2  
    7171                { 'r', "ringsize", "Number of threads in a cycle", ring_size }
    7272        };
    73         BENCH_OPT_PARSE("cforall cycle benchmark");
     73        BENCH_OPT_PARSE("libfibre cycle benchmark");
    7474
    7575        {
  • benchmark/readyQ/cycle.rs

    r42f6e07 r2b4daf2  
    1 #[cfg(any(
    2         feature = "sync time rt-threaded",
    3   ))]
    4 
    5 extern crate tokio;
    6 
    7 use std::io::{self, Write};
    81use std::sync::Arc;
    9 use std::sync::atomic::{AtomicU64, AtomicBool,Ordering};
    10 use std::time::{Instant,Duration};
     2use std::sync::atomic::Ordering;
     3use std::time::Instant;
    114
    125use tokio::runtime::Builder;
    136use tokio::sync;
    14 use tokio::time;
    157
    16 extern crate isatty;
    17 use isatty::stdout_isatty;
    18 
    19 extern crate num_format;
     8use clap::{Arg, App};
    209use num_format::{Locale, ToFormattedString};
    2110
    22 extern crate clap;
    23 use clap::{Arg, App};
     11#[path = "../bench.rs"]
     12mod bench;
    2413
    25 use std::cell::UnsafeCell;
    26 use std::mem::MaybeUninit;
    27 use std::ops;
    28 
    29 pub struct InitializeCell<T> {
    30     inner: UnsafeCell<MaybeUninit<T>>,
    31 }
    32 
    33 unsafe impl<T> Sync for InitializeCell<T> {}
    34 
    35 impl<T> InitializeCell<T> {
    36     pub const unsafe fn new_uninitialized() -> InitializeCell<T> {
    37           InitializeCell {
    38                 inner: UnsafeCell::new(MaybeUninit::uninit()),
    39           }
    40     }
    41     pub const fn new(init: T) -> InitializeCell<T> {
    42           InitializeCell {
    43                 inner: UnsafeCell::new(MaybeUninit::new(init)),
    44           }
    45     }
    46     pub unsafe fn init(&self, init: T) {
    47           (*self.inner.get()) = MaybeUninit::new(init);
    48     }
    49 }
    50 
    51 impl<T> ops::Deref for InitializeCell<T> {
    52     type Target = T;
    53     fn deref(&self) -> &T {
    54           unsafe {
    55                 &*(*self.inner.get()).as_ptr()
    56           }
    57     }
    58 }
    59 
    60 static CLOCK_MODE: InitializeCell<bool> = unsafe { InitializeCell::new_uninitialized() };
    61 static STOP_COUNT: InitializeCell<u64>  = unsafe { InitializeCell::new_uninitialized() };
    62 static DURATION: InitializeCell<f64>    = unsafe { InitializeCell::new_uninitialized() };
    63 static STOP         : AtomicBool = AtomicBool::new(false);
    64 static THREADS_LEFT : AtomicU64  = AtomicU64 ::new(10);
    65 
     14// ==================================================
    6615struct Partner {
    6716        sem: sync::Semaphore,
     
    6918}
    7019
    71 async fn partner_main(result: sync::oneshot::Sender<u64>, idx: usize, others: Arc<Vec<Arc<Partner>>> ) {
     20async fn partner_main(idx: usize, others: Arc<Vec<Arc<Partner>>>, exp: Arc<bench::BenchData> ) -> u64 {
    7221        let this = &others[idx];
    7322        let mut count:u64 = 0;
     
    7726                count += 1;
    7827
    79                 if  *CLOCK_MODE && STOP.load(Ordering::Relaxed) { break; }
    80                 if !*CLOCK_MODE && count >= *STOP_COUNT { break; }
     28                if  exp.clock_mode && exp.stop.load(Ordering::Relaxed) { break; }
     29                if !exp.clock_mode && count >= exp.stop_count { break; }
    8130        }
    8231
    83         THREADS_LEFT.fetch_sub(1, Ordering::SeqCst);
    84         result.send( count ).unwrap();
     32        exp.threads_left.fetch_sub(1, Ordering::SeqCst);
     33        count
    8534}
    8635
    87 fn prep(nthreads: usize, tthreads: usize) -> Vec<Arc<Partner>> {
    88         let mut thddata = Vec::with_capacity(tthreads);
    89         for i in 0..tthreads {
    90                 let pi = (i + nthreads) % tthreads;
    91                 thddata.push(Arc::new(Partner{
    92                         sem: sync::Semaphore::new(0),
    93                         next: pi,
    94                 }));
    95         }
    96         return thddata;
    97 }
    98 
    99 async fn wait(start: &Instant, is_tty: bool) {
    100         loop {
    101                 time::sleep(Duration::from_micros(100000)).await;
    102                 let delta = start.elapsed();
    103                 if is_tty {
    104                         print!(" {:.1}\r", delta.as_secs_f32());
    105                         io::stdout().flush().unwrap();
    106                 }
    107                 if *CLOCK_MODE && delta >= Duration::from_secs_f64(*DURATION)  {
    108                         break;
    109                 }
    110                 else if !*CLOCK_MODE && THREADS_LEFT.load(Ordering::Relaxed) == 0 {
    111                         break;
    112                 }
    113         }
    114 }
    115 
     36// ==================================================
    11637fn main() {
    11738        let options = App::new("Cycle Tokio")
    118                 .arg(Arg::with_name("duration")  .short("d").long("duration")  .takes_value(true).default_value("5").help("Duration of the experiments in seconds"))
    119                 .arg(Arg::with_name("iterations").short("i").long("iterations").takes_value(true).conflicts_with("duration").help("Number of iterations of the experiments"))
    120                 .arg(Arg::with_name("nthreads")  .short("t").long("nthreads")  .takes_value(true).default_value("1").help("Number of threads to use"))
    121                 .arg(Arg::with_name("nprocs")    .short("p").long("nprocs")    .takes_value(true).default_value("1").help("Number of processors to use"))
     39                .args(&bench::args())
    12240                .arg(Arg::with_name("ringsize")  .short("r").long("ringsize")  .takes_value(true).default_value("1").help("Number of threads in a cycle"))
    12341                .get_matches();
     
    12745        let nprocs    = options.value_of("nprocs").unwrap().parse::<usize>().unwrap();
    12846
    129         if options.is_present("iterations") {
    130                 unsafe{
    131                         CLOCK_MODE.init( false );
    132                         STOP_COUNT.init( options.value_of("iterations").unwrap().parse::<u64>().unwrap() );
    133                 }
    134         }
    135         else {
    136                 unsafe{
    137                         CLOCK_MODE.init(true);
    138                         DURATION  .init(options.value_of("duration").unwrap().parse::<f64>().unwrap());
    139                 }
    140         }
     47        let tthreads = nthreads * ring_size;
     48        let exp = Arc::new(bench::BenchData::new(options, tthreads));
    14149
    14250        let s = (1000000 as u64).to_formatted_string(&Locale::en);
    14351        assert_eq!(&s, "1,000,000");
    14452
    145 
    146         let tthreads = nthreads * ring_size;
    147         THREADS_LEFT.store(tthreads as u64, Ordering::SeqCst);
    148         let thddata = Arc::new(prep(nthreads, tthreads));
     53        let thddata : Arc<Vec<Arc<Partner>>> = Arc::new(
     54                (0..tthreads).map(|i| {
     55                        let pi = (i + nthreads) % tthreads;
     56                        Arc::new(Partner{
     57                                sem: sync::Semaphore::new(0),
     58                                next: pi,
     59                        })
     60                }).collect()
     61        );
    14962
    15063        let mut global_counter :u64 = 0;
     
    15770
    15871        runtime.block_on(async {
    159                 let mut result  : Vec<sync::oneshot::Receiver::<u64>> = Vec::with_capacity(tthreads);
    160                 {
    161                         let mut threads = Vec::with_capacity(tthreads);
    162                         for i in 0..tthreads {
    163                                 let (s, r) = sync::oneshot::channel::<u64>();
    164                                 result.push(r);
    165                                 threads.push(tokio::spawn(partner_main(s, i, thddata.clone())));
    166                         }
    167                         println!("Starting");
     72                let threads: Vec<_> = (0..tthreads).map(|i| {
     73                        tokio::spawn(partner_main(i, thddata.clone(), exp.clone()))
     74                }).collect();
     75                println!("Starting");
    16876
    169                         let is_tty = stdout_isatty();
    170                         let start = Instant::now();
     77                let start = Instant::now();
    17178
    172                         for i in 0..nthreads {
    173                                 thddata[i].sem.add_permits(1);
    174                         }
     79                for i in 0..nthreads {
     80                        thddata[i].sem.add_permits(1);
     81                }
    17582
    176                         wait(&start, is_tty).await;
     83                duration = exp.wait(&start).await;
    17784
    178                         STOP.store(true, Ordering::SeqCst);
    179                         duration = start.elapsed();
     85                println!("\nDone");
    18086
    181                         println!("\nDone");
     87                for i in 0..tthreads {
     88                        thddata[i].sem.add_permits(1);
     89                }
    18290
    183                         for i in 0..tthreads {
    184                                 thddata[i].sem.add_permits(1);
    185                         }
    186 
    187                         for _ in 0..tthreads {
    188                                 global_counter += result.pop().unwrap().await.unwrap();
    189                         }
     91                for t in threads {
     92                        global_counter += t.await.unwrap();
    19093                }
    19194        });
  • benchmark/readyQ/locality.go

    r42f6e07 r2b4daf2  
    22
    33import (
     4        "context"
    45        "flag"
    56        "fmt"
    67        "math/rand"
    78        "os"
     9        "syscall"
    810        "sync/atomic"
    911        "time"
     12        "unsafe"
     13        "golang.org/x/sync/semaphore"
    1014        "golang.org/x/text/language"
    1115        "golang.org/x/text/message"
    1216)
    1317
    14 func handshake(stop chan struct {}, c chan [] uint64, data [] uint64, share bool) (bool, [] uint64) {
    15         var s [] uint64 = data
    16         if !share {
    17                 s = nil
    18         }
    19 
    20         // send the data
    21         select {
    22         case <- stop:
    23                 return true, nil
    24         case c <- s:
    25         }
    26 
    27         // get the new data chunk
    28         select {
    29         case <- stop:
    30                 return true, nil
    31         case n := <- c:
    32                 if share {
    33                         return false, n
    34                 }
    35                 return false, data
    36         }
    37 }
    38 
    39 func local(result chan uint64, start chan struct{}, stop chan struct{}, size uint64, cnt uint64, channels []chan [] uint64, chan_cnt uint64, share bool) {
     18// ==================================================
     19type MyData struct {
     20        _p1 [16]uint64 // padding
     21        ttid int
     22        id int
     23        data [] uint64
     24        _p2 [16]uint64 // padding
     25}
     26
     27func NewData(id int, size uint64) (*MyData) {
    4028        var data [] uint64
    4129        data = make([]uint64, size)
     
    4331                data[i] = 0
    4432        }
    45         count := uint64(0)
     33        return &MyData{[16]uint64{0}, syscall.Gettid(), id, data,[16]uint64{0}}
     34}
     35
     36func (this * MyData) moved( ttid int ) (uint64) {
     37        if this.ttid == ttid {
     38                return 0
     39        }
     40        this.ttid = ttid
     41        return 1
     42}
     43
     44func (this * MyData) access( idx uint64 ) {
     45        this.data[idx % uint64(len(this.data))] += 1
     46}
     47
     48// ==================================================
     49type 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
     59func 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
     65func (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
     76type 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
     87func (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
     133func (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
     145type Result struct {
     146        count uint64
     147        gmigs uint64
     148        dmigs uint64
     149}
     150
     151func NewResult() (Result) {
     152        return Result{0, 0, 0}
     153}
     154
     155// ==================================================
     156// Random number generator, Go's native one is to slow and global
     157func __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
     168func 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
     175func 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
    46185        <- start
     186
     187        // Main loop
    47188        for true {
    48                 for i := uint64(0); i < cnt; i++ {
    49                         data[rand.Uint64() % size] += 1
    50                 }
    51 
    52                 i := rand.Uint64() % chan_cnt
     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))
    53194                var closed bool
    54                 closed, data = handshake(stop, channels[i], data, share)
    55                 count += 1
    56 
    57                 if  closed { break }
    58                 if !clock_mode && count >= stop_count { break }
    59         }
    60 
     195                data, closed = channels[i].put(&ctx, data, share)
     196
     197                // Check if the experiment is over
     198                if closed { break }                                       // yes, spot was closed
     199                if  clock_mode && atomic.LoadInt32(&stop) == 1 { break }  // yes, time's up
     200                if !clock_mode && r.count >= stop_count { break }         // yes, iterations reached
     201
     202                // Check everything is consistent
     203                if uint64(len(data.data)) != size { panic("Data has weird size") }
     204
     205                // write down progress and check migrations
     206                ttid := syscall.Gettid()
     207                r.count += 1
     208                r.gmigs += ctx .moved(ttid)
     209                r.dmigs += data.moved(ttid)
     210        }
     211
     212        // Mark goroutine as done
    61213        atomic.AddInt64(&threads_left, -1);
    62         result <- count
    63 }
    64 
     214
     215        // return result
     216        result <- r
     217}
     218
     219// ==================================================
     220// Program main
    65221func main() {
    66 
    67         work_sizeOpt := flag.Uint64("w", 2    , "Number of words (uint64) per threads")
    68         countOpt     := flag.Uint64("c", 2    , "Number of words (uint64) to touch")
     222        // Benchmark specific command line arguments
     223        nspotsOpt    := flag.Int   ("n", 0    , "Number of spots where threads sleep (nthreads - nspots are active at the same time)")
     224        work_sizeOpt := flag.Uint64("w", 2    , "Size of the array for each threads, in words (64bit)")
     225        countOpt     := flag.Uint64("c", 2    , "Number of words to touch when working (random pick, cells can be picked more than once)")
    69226        shareOpt     := flag.Bool  ("s", false, "Pass the work data to the next thread when blocking")
    70227
    71         bench_init()
    72 
     228        // General benchmark initialization and deinitialization
     229        defer bench_init()()
     230
     231        // Eval command line arguments
     232        nspots:= *nspotsOpt
    73233        size  := *work_sizeOpt
    74234        cnt   := *countOpt
    75235        share := *shareOpt
    76236
     237        if nspots == 0 { nspots = nthreads - nprocs; }
     238
     239        // Check params
    77240        if ! (nthreads > nprocs) {
    78241                fmt.Fprintf(os.Stderr, "Must have more threads than procs\n")
     
    80243        }
    81244
    82         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)
     245        // Make global data
     246        barrierStart := make(chan struct{})         // Barrier used at the start
     247        threads_left = int64(nthreads - nspots)                // Counter for active threads (not 'nthreads' because at all times 'nthreads - nprocs' are blocked)
     248        result  := make(chan Result)                // Channel for results
     249        channels := make([]Spot, nspots) // Number of spots
    87250        for i := range channels {
    88                 channels[i] = make(chan [] uint64, 1)
    89         }
    90 
     251                channels[i] = Spot{[16]uint64{0},uintptr(0), i,[16]uint64{0}}     // init spots
     252        }
     253
     254        // start the goroutines
    91255        for i := 0; i < nthreads; i++ {
    92                 go local(result, barrierStart, barrierStop, size, cnt, channels, uint64(nthreads - nprocs), share)
     256                go local(result, barrierStart, size, cnt, channels, share, i)
    93257        }
    94258        fmt.Printf("Starting\n");
    95259
     260        atomic.StoreInt32(&stop, 0)
    96261        start := time.Now()
    97         close(barrierStart)
    98 
    99         wait(start, true);
    100 
    101         close(barrierStop)
     262        close(barrierStart) // release barrier
     263
     264        wait(start, true);  // general benchmark wait
     265
     266        atomic.StoreInt32(&stop, 1)
    102267        end := time.Now()
    103268        delta := end.Sub(start)
     
    105270        fmt.Printf("\nDone\n")
    106271
    107         global_counter := uint64(0)
     272        // release all the blocked threads
     273        for i := range channels {
     274                channels[i].release()
     275        }
     276
     277        // Join and accumulate results
     278        results := NewResult()
    108279        for i := 0; i < nthreads; i++ {
    109                 global_counter += <- result
    110         }
    111 
     280                r := <- result
     281                results.count += r.count
     282                results.gmigs += r.gmigs
     283                results.dmigs += r.dmigs
     284        }
     285
     286        // Print with nice 's, i.e. 1'000'000 instead of 1000000
    112287        p := message.NewPrinter(language.English)
    113         p.Printf("Duration (ms)          : %f\n", delta.Seconds());
     288        p.Printf("Duration (s)           : %f\n", delta.Seconds());
    114289        p.Printf("Number of processors   : %d\n", nprocs);
    115290        p.Printf("Number of threads      : %d\n", nthreads);
    116291        p.Printf("Work size (64bit words): %d\n", size);
    117         p.Printf("Total Operations(ops)  : %15d\n", global_counter)
    118         p.Printf("Ops per second         : %18.2f\n", float64(global_counter) / delta.Seconds())
    119         p.Printf("ns per ops             : %18.2f\n", float64(delta.Nanoseconds()) / float64(global_counter))
    120         p.Printf("Ops per threads        : %15d\n", global_counter / uint64(nthreads))
    121         p.Printf("Ops per procs          : %15d\n", global_counter / uint64(nprocs))
    122         p.Printf("Ops/sec/procs          : %18.2f\n", (float64(global_counter) / float64(nprocs)) / delta.Seconds())
    123         p.Printf("ns per ops/procs       : %18.2f\n", float64(delta.Nanoseconds()) / (float64(global_counter) / float64(nprocs)))
    124 }
     292        p.Printf("Total Operations(ops)  : %15d\n", results.count)
     293        p.Printf("Total G Migrations     : %15d\n", results.gmigs)
     294        p.Printf("Total D Migrations     : %15d\n", results.dmigs)
     295        p.Printf("Ops per second         : %18.2f\n", float64(results.count) / delta.Seconds())
     296        p.Printf("ns per ops             : %18.2f\n", float64(delta.Nanoseconds()) / float64(results.count))
     297        p.Printf("Ops per threads        : %15d\n", results.count / uint64(nthreads))
     298        p.Printf("Ops per procs          : %15d\n", results.count / uint64(nprocs))
     299        p.Printf("Ops/sec/procs          : %18.2f\n", (float64(results.count) / float64(nprocs)) / delta.Seconds())
     300        p.Printf("ns per ops/procs       : %18.2f\n", float64(delta.Nanoseconds()) / (float64(results.count) / float64(nprocs)))
     301}
  • benchmark/readyQ/rq_bench.hfa

    r42f6e07 r2b4daf2  
    3939                } else if(stop_count > 0) { \
    4040                        clock_mode = false; \
    41                         printf("Running for %lu iterations\n", stop_count); \
     41                        printf("Running for %llu iterations\n", stop_count); \
    4242                } else { \
    4343                        duration = 5; clock_mode = true;\
  • benchmark/readyQ/rq_bench.hpp

    r42f6e07 r2b4daf2  
    9797}
    9898
     99bool 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
    99113bool parse_settrue (const char *, bool & value ) {
    100114        value = true;
     
    226240        {
    227241                int idx = 0;
    228                 for(int i = 0; i < opt_count; i++) {
     242                for(size_t i = 0; i < opt_count; i++) {
    229243                        if(options[i].long_name) {
    230244                                optarr[idx].name = options[i].long_name;
     
    256270        {
    257271                int idx = 0;
    258                 for(int i = 0; i < opt_count; i++) {
     272                for(size_t i = 0; i < opt_count; i++) {
    259273                        optstring[idx] = options[i].short_name;
    260274                        idx++;
     
    279293                        case 'h':
    280294                                out = stdout;
     295                                [[fallthrough]];
    281296                        case '?':
    282297                                usage(argv[0], options, opt_count, usage_msg, out);
    283298                        default:
    284                                 for(int i = 0; i < opt_count; i++) {
     299                                for(size_t i = 0; i < opt_count; i++) {
    285300                                        if(opt == options[i].short_name) {
    286301                                                const char * arg = optarg ? optarg : "";
     302                                                if( arg[0] == '=' ) { arg++; }
    287303                                                bool success = options[i].parse_fun( arg, options[i].variable );
    288304                                                if(success) goto NEXT_ARG;
     
    319335        int width = 0;
    320336        {
    321                 for(int i = 0; i < opt_count; i++) {
     337                for(size_t i = 0; i < opt_count; i++) {
    322338                        if(options[i].long_name) {
    323339                                int w = strlen(options[i].long_name);
     
    338354        fprintf(out, "Usage:\n  %s %s\n", cmd, help);
    339355
    340         for(int i = 0; i < opt_count; i++) {
     356        for(size_t i = 0; i < opt_count; i++) {
    341357                printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help);
    342358        }
  • configure.ac

    r42f6e07 r2b4daf2  
    295295# Some of our makefile don't need to be distributed
    296296AM_CONDITIONAL([CFORALL_DISTRIBUTE], [test -e $TOP_SRCDIR/autogen.sh])
    297 AM_COND_IF([CFORALL_DISTRIBUTE],
    298         [AC_CONFIG_FILES([
     297AM_COND_IF([CFORALL_DISTRIBUTE], [
     298        AC_CONFIG_FILES([
    299299                longrun_tests/Makefile
    300300                benchmark/Makefile
     
    302302                tools/Makefile
    303303                tools/prettyprinter/Makefile
    304                 ])])
     304        ])
     305
     306        AC_OUTPUT(benchmark/Cargo.toml)
     307])
    305308
    306309AC_CONFIG_LINKS([tests/test.py:tests/test.py])
  • doc/theses/thierry_delisle_PhD/thesis/Makefile

    r42f6e07 r2b4daf2  
    2828        base \
    2929        empty \
     30        emptybit \
     31        emptytls \
     32        emptytree \
     33        fairness \
    3034        system \
    3135}
     
    7377        ${LaTeX} $<
    7478
     79build/fairness.svg : fig/fairness.py | ${Build}
     80        python3 $< $@
     81
    7582## Define the default recipes.
    7683
     
    8895        fig2dev -L pstex_t -p ${Build}/$@ $< > ${Build}/$@_t
    8996
     97%.pstex : build/%.svg | ${Build}
     98        inkscape -z -D --file=$< --export-eps=${Build}/$@ --export-latex
     99        mv ${Build}/$@_tex ${Build}/$@_t
     100        echo "sed -i 's/$@/${Build}/$@/g' ${Build}/$@_t"
     101        sed -i 's/$@/${Build}\/$@/g' ${Build}/$@_t
     102
    90103## pstex with inverted colors
    91104%.dark.pstex : fig/%.fig Makefile | ${Build}
  • doc/theses/thierry_delisle_PhD/thesis/glossary.tex

    r42f6e07 r2b4daf2  
    77\newacronym{io}{I/O}{Input and Output}
    88\newacronym{numa}{NUMA}{Non-Uniform Memory Access}
     9\newacronym{prng}{PRNG}{Pseudo Random Number Generator}
    910\newacronym{raii}{RAII}{Resource Acquisition Is Initialization}
    1011\newacronym{tls}{TLS}{Thread Local Storage}
  • doc/theses/thierry_delisle_PhD/thesis/local.bib

    r42f6e07 r2b4daf2  
    609609  note = "[Online; accessed 23-October-2020]"
    610610}
     611
     612@misc{wiki:lcg,
     613  author = "{Wikipedia contributors}",
     614  title = "Linear congruential generator --- {W}ikipedia{,} The Free Encyclopedia",
     615  year = "2020",
     616  url = "https://en.wikipedia.org/wiki/Linear_congruential_generator",
     617  note = "[Online; accessed 2-January-2021]"
     618}
  • doc/theses/thierry_delisle_PhD/thesis/text/core.tex

    r42f6e07 r2b4daf2  
    11\chapter{Scheduling Core}\label{core}
    22
    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.
     3Before 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.
    44
    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.
     5I believe it is important to discuss the steady state first because it is the easiest case to handle and, relatedly, the case in which the best performance is to be expected. As such, when the system is either overloaded or underloaded, a common approach is to try to adapt the system to the new load and return to the steady state, \eg, adding or removing workers. Flaws in the scheduling when the system is in the steady state can therefore to be pervasive in all states.
    66
    77\section{Design Goals}
    8 As with most of the design decisions behind \CFA, the main goal is to match the expectation of the programmer, according to their probable mental model. To match these expectations, the design must offer the programmers sufficient guarantees so that, as long as the programmer respects the mental model, the system will also respect this model.
     8As with most of the design decisions behind \CFA, an important goal is to match the expectation of the programmer, according to their probable mental model. To match these expectations, the design must offer the programmers sufficient guarantees so that, as long as they respect the mental model, the system will also respect this model.
    99
    1010For threading, a simple and common mental model is the ``Ideal multi-tasking CPU'' :
     
    1616Applied 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.
    1717
    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:
     18In general, the expectation at the center of this model is that ready \glspl{thrd} do not interfere with eachother but simply share the hardware. This makes it easier to reason about threading because ready \glspl{thrd} can be taken in isolation and the effect of the scheduler can be virtually ignored. This expectation of \gls{thrd} independence means the scheduler is expected to offer two guarantees:
    1919\begin{enumerate}
    2020        \item A fairness guarantee: a \gls{thrd} that is ready to run will not be prevented to do so by another thread.
     
    2424It 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.
    2525
    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.
     26Similarly 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.
    2727
    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}.
     28More 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}
     35An 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
     37For 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
     39However, 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}
    3050
    3151\section{Design}
    32 While avoiding the pitfalls of Feedback Scheduling is fairly easy, scheduling does not innately require feedback, avoiding prioritization of \glspl{thrd} is more difficult because of implicitly priorities, see Subsection~\ref{priority}. A strictly \glsxtrshort{fifo} rea
     52A 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.
    3353
    34 \subsection{Sharding}
     54\subsection{Sharding} \label{sec:sharding}
     55An 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.
    3556
    3657\begin{figure}
     
    4566
    4667\subsection{Finding threads}
    47 Once threads have been distributed onto multiple queues, indentifying which queues are empty and which aren't can become a problem.
    48 Indeed, if the number of \glspl{thrd} does not far exceed the number of queues, it is probable that several of these queues are empty.
    49 Figure~\ref{fig:empty} shows an example with 2 \glspl{thrd} running on 8 queues, where the chances of getting an empty queue is 75\% per pick, meaning two random picks yield a \gls{thrd} only half the time.
     68Once 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.
    5069
    5170
     
    6180This 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.
    6281
    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.
     82Solutions 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:
    6483
    65 \paragraph{Dense Information}
     84\begin{figure}
     85        \begin{center}
     86                {\resizebox{0.73\textwidth}{!}{\input{emptybit.pstex_t}}}
     87        \end{center}
     88        \vspace*{-5pt}
     89        \caption{Underloaded queue with added bitmask to indicate which array cells have items.}
     90        \label{fig:emptybit}
     91        \begin{center}
     92                {\resizebox{0.73\textwidth}{!}{\input{emptytree.pstex_t}}}
     93        \end{center}
     94        \vspace*{-5pt}
     95        \caption{Underloaded queue with added binary search tree indicate which array cells have items.}
     96        \label{fig:emptytree}
     97        \begin{center}
     98                {\resizebox{0.9\textwidth}{!}{\input{emptytls.pstex_t}}}
     99        \end{center}
     100        \vspace*{-5pt}
     101        \caption{Underloaded queue with added per processor bitmask to indicate which array cells have items.}
     102        \label{fig:emptytls}
     103\end{figure}
     104
     105\paragraph{Dense Information} Figure~\ref{fig:emptybit} shows a dense bitmask to identify which inner queues are currently in use. This approach means processors can often find \glspl{thrd} in constant time, regardless of how many underlying queues are empty. Furthermore, modern x86 CPUs have extended bit manipulation instructions (BMI2) that allow using the bitmask with very little overhead compared to the randomized selection approach for a filled ready queue, offering good performance even in cases with many empty inner queues. However, this technique has its limits: with a single word\footnote{Word refers here to however many bits can be written atomically.} bitmask, the total number of underlying queues in the ready queue is limited to the number of bits in the word. With a multi-word bitmask, this maximum limit can be increased arbitrarily, but the look-up will nolonger be constant time. Finally, a dense bitmap, either single or multi-word, causes additional contention problems which reduces performance because of cache misses after updates. This central update bottleneck also means the information in the bitmask is more often stale before a processor can use it to find an item, \ie mask read says there are available \glspl{thrd} but none on queue.
     106
     107\paragraph{Sparse Information} Figure~\ref{fig:emptytree} shows an approach using a hierarchical tree data-structure to reduce contention and has been shown to work in similar cases~\cite{ellen2007snzi}. However, this approach may lead to poorer performance due to the inherent pointer chasing cost while still allowing more contention on the nodes of the tree if the tree is not deep enough.
     108
     109\paragraph{Local Information} Figure~\ref{fig:emptytls} shows an approach using dense information, similar to the bitmap, but have each thread keep its own independent copy of it. While this approach can offer good scalability \emph{and} low latency, the liveliness and discovery of the information can become a problem. This case is made worst in systems with few processors where even blind random picks can find \glspl{thrd} in few tries.
     110
     111I 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/}
     114In 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
     116To 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
     118The 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
     128The main benefit of this technique is that it basically repects the desired properties of Figure~\ref{fig:fair}. When looking for work, \glspl{proc} will look first at the last cells they pushed to, if any, and then move backwards through the cells. As the \glspl{proc} continue looking for work, $F$ moves back and $B$ stays in place. As a result the relation between the two becomes weaker, which means that the probablisitic fairness of the algorithm reverts to normal. Chapter~\ref{proofs} discusses more formally the fairness guarantees of this algorithm.
     129
     130\section{Details}
  • doc/theses/thierry_delisle_PhD/thesis/text/existing.tex

    r42f6e07 r2b4daf2  
    4242\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}
    4343
    44 \TODO{The survey is not great on this subject}
     44\todo{The survey is not great on this subject}
    4545
    4646\paragraph{Complex Machine Architecture} Another aspect that has been looked at is how well Work Stealing is applicable to different machine architectures.
  • libcfa/src/Makefile.am

    r42f6e07 r2b4daf2  
    5858        concurrency/iofwd.hfa \
    5959        containers/list.hfa \
    60         containers/stackLockFree.hfa
     60        containers/stackLockFree.hfa \
     61        vec/vec.hfa \
     62        vec/vec2.hfa \
     63        vec/vec3.hfa \
     64        vec/vec4.hfa
    6165
    6266inst_headers_src = \
     
    9498        concurrency/clib/cfathread.h \
    9599        concurrency/invoke.h \
     100        concurrency/future.hfa \
    96101        concurrency/kernel/fwd.hfa
    97102
  • libcfa/src/bits/collection.hfa

    r42f6e07 r2b4daf2  
    11#pragma once
     2#include <stdio.h> // REMOVE THIS AFTER DEBUGGING
     3
    24
    35struct Colable {
    4         Colable * next;                                                                         // next node in the list
     6        struct Colable * next;                                                                          // next node in the list
    57        // invariant: (next != 0) <=> listed()
    68};
    7 
    8 inline {
     9#ifdef __cforall
     10static inline {
    911        // PUBLIC
    1012
     
    2830        }
    2931
    30         // wrappers to make Collection have T
    31         forall( dtype T ) {
    32                 T *& Next( T * n ) {
    33                         return (T *)Next( (Colable *)n );
    34                 }
    35 
    36                 bool listed( T * n ) {
    37                         return Next( (Colable *)n ) != 0p;
    38                 }
    39         } // distribution
     32        // // wrappers to make Collection have T
     33        // forall( dtype T ) {
     34        //      T *& Next( T * n ) {
     35        //              return (T *)Next( (Colable *)n );
     36        //      }
     37        // } // distribution
    4038} // distribution
    4139
     40forall( dtype T | { T *& Next ( T * ); } ) {
     41        bool listed( T * n ) {
     42                return Next( n ) != 0p;
     43        }
     44}
    4245
    4346struct Collection {
     
    4548};
    4649
    47 inline {
     50static inline {
    4851        // class invariant: root == 0 & empty() | *root in *this
    4952        void ?{}( Collection &, const Collection & ) = void; // no copy
     
    6568
    6669struct ColIter {
    67         void * curr;                                                                            // element to be returned by >>
     70        void * curr;                                                                            // element returned by |
    6871};
    6972
    70 inline {
     73static inline {
    7174        void ?{}( ColIter & colIter ) with( colIter ) {
    7275                curr = 0p;
     
    7982        } // distribution
    8083} // distribution
     84#endif
  • libcfa/src/bits/containers.hfa

    r42f6e07 r2b4daf2  
    3636        #define __small_array_t(T) __small_array(T)
    3737#else
    38         #define __small_array_t(T) struct __small_array
     38        #define __small_array_t(T) __small_array
    3939#endif
    4040
  • libcfa/src/bits/defs.hfa

    r42f6e07 r2b4daf2  
    2929#define __cfa_anonymous_object(x) inline struct x
    3030#else
    31 #define __cfa_anonymous_object(x) x __cfa_anonymous_object
     31#define __cfa_anonymous_object(x) struct x __cfa_anonymous_object
    3232#endif
    3333
  • libcfa/src/bits/locks.hfa

    r42f6e07 r2b4daf2  
    283283                void ^?{}(future_t &) {}
    284284
     285                void reset(future_t & this) {
     286                        // needs to be in 0p or 1p
     287                        __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);
     288                }
     289
    285290                // check if the future is available
    286291                bool available( future_t & this ) {
     
    340345
    341346                // Mark the future as abandoned, meaning it will be deleted by the server
    342                 void abandon( future_t & this ) {
     347                bool abandon( future_t & this ) {
     348                        /* paranoid */ verify( this.ptr != 3p );
     349
     350                        // Mark the future as abandonned
    343351                        struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST);
     352
     353                        // If the future isn't already fulfilled, let the server delete it
     354                        if( got == 0p ) return false;
    344355
    345356                        // got == 2p: the future is ready but the context hasn't fully been consumed
     
    347358                        if( got == 2p ) {
    348359                                while( this.ptr != 1p ) Pause();
    349                         }
    350                         return;
     360                                got = 1p;
     361                        }
     362
     363                        // The future is completed delete it now
     364                        /* paranoid */ verify( this.ptr != 1p );
     365                        free( &this );
     366                        return true;
    351367                }
    352368
  • libcfa/src/bits/queue.hfa

    r42f6e07 r2b4daf2  
    33#include "bits/collection.hfa"
    44
    5 forall( dtype T ) {
     5// A Queue(T) is a Collection(T) defining the ordering that nodes are returned by drop() in the same order from those
     6// added by add(). T must be a public descendant of uColable.
     7
     8// The implementation is a typical singly-linked list, except the next field of the last element points to itself
     9// instead of being null.
     10
     11forall( dtype T | { T *& Next ( T * ); } ) {
    612        struct Queue {
    713                inline Collection;                                                              // Plan 9 inheritance
     
    3440                } // post: n == tail() & succ(n) == 0 | n != tail() & *succ(n) in *q
    3541
    36                 void addHead( Queue(T) & q, T & n ) with( q ) {
     42                T & addHead( Queue(T) & q, T & n ) with( q ) {
    3743                        #ifdef __CFA_DEBUG__
    3844                        if ( listed( &n ) ) abort( "(Queue &)%p.addHead( %p ) : Node is already on another list.", &q, &n );
     
    4551                                Next( &n ) = &n;                                                // last node points to itself
    4652                        }
     53                        return n;
    4754                }
    4855
    49                 void addTail( Queue(T) & q, T & n ) with( q ) {
     56                T & addTail( Queue(T) & q, T & n ) with( q ) {
    5057                        #ifdef __CFA_DEBUG__
    5158                        if ( listed( &n ) ) abort( "(Queue &)%p.addTail( %p ) : Node is already on another list.", &q, &n );
     
    5562                        last = &n;
    5663                        Next( &n ) = &n;                                                        // last node points to itself
     64                        return n;
    5765                }
    5866
    59                 void add( Queue(T) & q, T & n ) with( q ) {
    60                         addTail( q, n );
     67                T & add( Queue(T) & q, T & n ) with( q ) {
     68                        return addTail( q, n );
    6169                }
    6270
     
    6472                        T & t = head( q );
    6573                        if ( root ) {
    66                                 root = Next( root );
     74                                root = Next( (T *)root );
    6775                                if ( &head( q ) == &t ) {
    6876                                        root = last = 0p;                                       // only one element
     
    7785                }
    7886
    79                 void remove( Queue(T) & q, T & n ) with( q ) {  // O(n)
     87                T & remove( Queue(T) & q, T & n ) with( q ) {   // O(n)
    8088                        #ifdef __CFA_DEBUG__
    8189                        if ( ! listed( (Colable &)n ) ) abort( "(Queue &)%p.remove( %p ) : Node is not on a list.", &q, &n );
     
    103111                                curr = Next( curr );
    104112                        }
     113                        return n;
    105114                } // post: ! listed( n )
    106115
    107                 T & dropTail( Queue(T) & q ) with( q ) { // O(n)
     116                T & dropTail( Queue(T) & q ) with( q ) {                // O(n)
    108117                        T & n = tail( q );
    109118                        return &n ? remove( q, n ), n : *0p;
     
    142151} // distribution
    143152
    144 forall( dtype T ) {
     153forall( dtype T | { T *& Next ( T * ); } ) {
    145154        struct QueueIter {
    146155                inline ColIter;                                                                 // Plan 9 inheritance
     
    152161                } // post: curr == 0p
    153162
    154                 // create an iterator active in Queue q
     163                // create an iterator active in queue q
    155164                void ?{}( QueueIter(T) & qi, Queue(T) & q ) with( qi ) {
    156165                        curr = &head( q );
     
    161170                } // post: curr = {e in q}
    162171
    163                 // make existing iterator active in Queue q
     172                // make existing iterator active in queue q
    164173                void over( QueueIter(T) & qi, Queue(T) & q ) with( qi ) {
    165174                        curr = &head( q );
    166175                } // post: curr = {e in q}
    167176
    168                 bool ?>>?( QueueIter(T) & qi, T && tp ) with( qi ) {
     177                bool ?|?( QueueIter(T) & qi, T && tp ) with( qi ) {
    169178                        if ( curr ) {
    170179                                &tp = Curr( qi );
     
    174183                        return &tp != 0p;
    175184                }
    176                 // post: elts == null & !operator>>(tp) | elts != null & *tp' in elts & elts' == elts - *tp & operator>>(tp)
     185                // post: elts == null & !operator|(tp) | elts != null & *tp' in elts & elts' == elts - *tp & operator|(tp)
    177186        } // distribution
    178187} // distribution
  • libcfa/src/bits/sequence.hfa

    r42f6e07 r2b4daf2  
    22
    33#include "bits/collection.hfa"
     4#include "bits/defs.hfa"
    45
    56struct Seqable {
    6         inline Colable;
    7         Seqable * back;                                                                         // pointer to previous node in the list
     7        __cfa_anonymous_object(Colable);
     8        struct Seqable * back;                                                                          // pointer to previous node in the list
    89};
    910
    10 inline {
     11#ifdef __cforall
     12static inline {
    1113        // PUBLIC
    1214
     
    2628        }
    2729
    28         // wrappers to make Collection have T
    29         forall( dtype T ) {
    30                 T *& Back( T * n ) {
    31                         return (T *)Back( (Seqable *)n );
    32                 }
    33         } // distribution
     30        // // wrappers to make Collection have T
     31        // forall( dtype T ) {
     32        //      T *& Back( T * n ) {
     33        //              return (T *)Back( (Seqable *)n );
     34        //      }
     35        // } // distribution
    3436} // distribution
    3537
    36 forall( dtype T ) {
     38
     39// A Sequence(T) is a Collection(T) defining the ordering of a uStack and uQueue, and to insert and remove elements
     40// anywhere in the sequence. T must be a public descendant of uSeqable.
     41
     42// The implementation is a typical doubly-linked list, except the next field of the last node points at the first node
     43// and the back field of the last node points at the first node (circular).
     44
     45forall( dtype T | { T *& Back ( T * ); T *& Next ( T * ); } ) {
    3746        struct Sequence {
    3847                inline Collection;                                                              // Plan 9 inheritance
    3948        };
    4049
    41         inline {
     50        static inline {
    4251                // wrappers to make Collection have T
    4352                T & head( Sequence(T) & s ) with( s ) {
     
    5059                void ?{}( Sequence(T) & s ) with( s ) {
    5160                        ((Collection &)s){};
    52                 }       // post: isEmpty().
    53 
    54                 // Return a pointer to the last sequence element, without removing it. 
     61                }       // post: isEmpty()
     62
     63                // Return a pointer to the last sequence element, without removing it.
    5564                T & tail( Sequence(T) & s ) with( s ) {
    5665                        return root ? (T &)*Back( &head( s ) ) : *0p;
     
    6574                } // post: n == tail() & succ(n) == 0 | n != tail() & *succ(n) in *s
    6675
    67                 // Return a pointer to the element before *n, or 0p if there isn't one.
     76                // Return a pointer to the element before *n, or 0p if list empty.
    6877                T * pred( Sequence(T) & s, T * n ) with( s ) {  // pre: *n in *s
    6978                        #ifdef __CFA_DEBUG__
     
    7180                        #endif // __CFA_DEBUG__
    7281                        return n == &head( s ) ? 0p : Back( n );
    73                 }       // 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
     82                } // post: n == head() & head(n) == 0 | n != head() & *pred(n) in *s
     83
     84
     85                // Insert *n into the sequence before *bef, or at the end if bef == 0p.
     86                T & insertBef( Sequence(T) & s, T & n, T & bef ) with( s ) { // pre: !n->listed() & *bef in *s
    7887                        #ifdef __CFA_DEBUG__
    7988                        if ( listed( &n ) ) abort( "(Sequence &)%p.insertBef( %p, %p ) : Node is already on another list.", &s, n, &bef );
     
    103112                                Next( Back( &n ) ) = &n;
    104113                        } // if
     114                        return n;
    105115                }       // post: n->listed() & *n in *s & succ(n) == bef
    106116
    107117
    108118                // Insert *n into the sequence after *aft, or at the beginning if aft == 0.
    109                 void insertAft( Sequence(T) & s, T & aft, T & n ) with( s ) {   // pre: !n->listed() & *aft in *s
     119                T & insertAft( Sequence(T) & s, T & aft, T & n ) with( s ) {    // pre: !n->listed() & *aft in *s
    110120                        #ifdef __CFA_DEBUG__
    111121                        if ( listed( &n ) ) abort( "(Sequence &)%p.insertAft( %p, %p ) : Node is already on another list.", &s, &aft, &n );
     
    133143                                Next( &aft ) = &n;
    134144                        } // if
    135                 }         // post: n->listed() & *n in *s & succ(n) == bef
     145                        return n;
     146                } // post: n->listed() & *n in *s & succ(n) == bef
    136147               
    137148                // pre: n->listed() & *n in *s
    138                 void remove( Sequence(T) & s, T & n ) with( s ) { // O(1)
     149                T & remove( Sequence(T) & s, T & n ) with( s ) { // O(1)
    139150                        #ifdef __CFA_DEBUG__
    140151                        if ( ! listed( &n ) ) abort( "(Sequence &)%p.remove( %p ) : Node is not on a list.", &s, &n );
     
    147158                        Next( Back( &n ) ) = Next( &n );
    148159                        Next( &n ) = Back( &n ) = 0p;
    149                 }                                                       // post: !n->listed().
     160                        return n;
     161                } // post: !n->listed()
    150162
    151163                // Add an element to the head of the sequence.
    152                 void addHead( Sequence(T) & s, T & n ) {                // pre: !n->listed(); post: n->listed() & head() == n
    153                         insertAft( s, *0p, n );
    154                 }
     164                T & addHead( Sequence(T) & s, T & n ) {                 // pre: !n->listed(); post: n->listed() & head() == n
     165                        return insertAft( s, *0p, n );
     166                }
     167
    155168                // Add an element to the tail of the sequence.
    156                 void addTail( Sequence(T) & s, T & n ) {                // pre: !n->listed(); post: n->listed() & head() == n
    157                         insertBef( s, n, *0p );
    158                 }
     169                T & addTail( Sequence(T) & s, T & n ) {                 // pre: !n->listed(); post: n->listed() & head() == n
     170                        return insertBef( s, n, *0p );
     171                }
     172
    159173                // Add an element to the tail of the sequence.
    160                 void add( Sequence(T) & s, T & n ) {                    // pre: !n->listed(); post: n->listed() & head() == n
    161                         addTail( s, n );
    162                 }
     174                T & add( Sequence(T) & s, T & n ) {                             // pre: !n->listed(); post: n->listed() & head() == n
     175                        return addTail( s, n );
     176                }
     177
    163178                // Remove and return the head element in the sequence.
    164179                T & dropHead( Sequence(T) & s ) {
     
    166181                        return &n ? remove( s, n ), n : *0p;
    167182                }
     183
    168184                // Remove and return the head element in the sequence.
    169185                T & drop( Sequence(T) & s ) {
    170186                        return dropHead( s );
    171187                }
     188
    172189                // Remove and return the tail element in the sequence.
    173190                T & dropTail( Sequence(T) & s ) {
     
    184201                                T * toEnd = Back( &head( s ) );
    185202                                T * fromEnd = Back( &head( from ) );
    186                                 Back( root ) = fromEnd;
     203                                Back( (T *)root ) = fromEnd;
    187204                                Next( fromEnd ) = &head( s );
    188                                 Back( from.root ) = toEnd;
     205                                Back( (T *)from.root ) = toEnd;
    189206                                Next( toEnd ) = &head( from );
    190207                        } // if
     
    214231} // distribution
    215232
    216 forall( dtype T ) {
     233forall( dtype T | { T *& Back ( T * ); T *& Next ( T * ); } ) {
    217234        // SeqIter(T) is used to iterate over a Sequence(T) in head-to-tail order.
    218235        struct SeqIter {
     
    224241        };
    225242
    226         inline {
     243        static inline {
    227244                void ?{}( SeqIter(T) & si ) with( si ) {
    228245                        ((ColIter &)si){};
    229246                        seq = 0p;
    230                 } // post: elts = null.
    231 
     247                } // post: elts = null
     248
     249                // Create a iterator active in sequence s.
    232250                void ?{}( SeqIter(T) & si, Sequence(T) & s ) with( si ) {
    233251                        ((ColIter &)si){};
    234252                        seq = &s;
    235253                        curr = &head( s );
    236                 } // post: elts = null.
     254                } // post: elts = null
    237255
    238256                void ?{}( SeqIter(T) & si, Sequence(T) & s, T & start ) with( si ) {
     
    240258                        seq = &s;
    241259                        curr = &start;
    242                 } // post: elts = null.
    243 
     260                } // post: elts = null
     261
     262                // Make the iterator active in sequence s.
    244263                void over( SeqIter(T) & si, Sequence(T) & s ) with( si ) {
    245264                        seq = &s;
    246265                        curr = &head( s );
    247                 } // post: elts = {e in s}.
    248 
    249                 bool ?>>?( SeqIter(T) & si, T && tp ) with( si ) {
     266                } // post: elts = {e in s}
     267
     268                bool ?|?( SeqIter(T) & si, T && tp ) with( si ) {
    250269                        if ( curr ) {
    251270                                &tp = Curr( si );
     
    265284        };
    266285
    267         inline {
     286        static inline {
    268287                void ?{}( SeqIterRev(T) & si ) with( si ) {     
    269288                        ((ColIter &)si){};
    270289                        seq = 0p;
    271                 } // post: elts = null.
    272 
     290                } // post: elts = null
     291
     292                // Create a iterator active in sequence s.
    273293                void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) {   
    274294                        ((ColIter &)si){};
    275295                        seq = &s;
    276296                        curr = &tail( s );
    277                 } // post: elts = null.
     297                } // post: elts = null
    278298
    279299                void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) {
     
    281301                        seq = &s;
    282302                        curr = &start;
    283                 } // post: elts = null.
    284 
     303                } // post: elts = null
     304
     305                // Make the iterator active in sequence s.
    285306                void over( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) {
    286307                        seq = &s;
    287308                        curr = &tail( s );
    288                 } // post: elts = {e in s}.
    289 
    290                 bool ?>>?( SeqIterRev(T) & si, T && tp ) with( si ) {
     309                } // post: elts = {e in s}
     310
     311                bool ?|?( SeqIterRev(T) & si, T && tp ) with( si ) {
    291312                        if ( curr ) {
    292313                                &tp = Curr( si );
     
    298319        } // distribution
    299320} // distribution
     321
     322#endif
  • libcfa/src/bits/stack.hfa

    r42f6e07 r2b4daf2  
    33#include "bits/collection.hfa"
    44
    5 forall( dtype T ) {
     5// A Stack(T) is a Collection(T) defining the ordering that nodes are returned by drop() in the reverse order from those
     6// added by add(). T must be a public descendant of uColable.
     7
     8// The implementation is a typical singly-linked list, except the next field of the last element points to itself
     9// instead of being null.
     10
     11forall( dtype T | { T *& Next ( T * ); } ) {
    612        struct Stack {
    713                inline Collection;                                                              // Plan 9 inheritance
     
    2531                }
    2632
    27                 void addHead( Stack(T) & s, T & n ) with( s ) {
     33                T & addHead( Stack(T) & s, T & n ) with( s ) {
    2834                        #ifdef __CFA_DEBUG__
    2935                        if ( listed( (Colable &)(n) ) ) abort( "(Stack &)%p.addHead( %p ) : Node is already on another list.", &s, n );
     
    3137                        Next( &n ) = &head( s ) ? &head( s ) : &n;
    3238                        root = &n;
     39                        return n;
    3340                }
    3441
    35                 void add( Stack(T) & s, T & n ) with( s ) {
    36                         addHead( s, n );
     42                T & add( Stack(T) & s, T & n ) with( s ) {
     43                        return addHead( s, n );
    3744                }
    3845
    39                 void push( Stack(T) & s, T & n ) with( s ) {
    40                         addHead( s, n );
     46                T & push( Stack(T) & s, T & n ) with( s ) {
     47                        return addHead( s, n );
    4148                }
    4249
     
    4451                        T & t = head( s );
    4552                        if ( root ) {
    46                                 root = ( T *)Next( root );
     53                                root = ( T *)Next( (T *)root );
    4754                                if ( &head( s ) == &t ) root = 0p;              // only one element ?
    4855                                Next( &t ) = 0p;
     
    5764} // distribution
    5865
     66// A StackIter(T) is a subclass of ColIter(T) that generates the elements of a Stack(T).  It returns the elements in the
     67// order returned by drop().
    5968
    60 forall( dtype T ) {
     69forall( dtype T | { T *& Next ( T * ); } ) {
    6170        struct StackIter {
    6271                inline ColIter;                                                                 // Plan 9 inheritance
     
    6877                } // post: curr == 0p
    6978
    70                 // create an iterator active in Stack s
     79                // create an iterator active in stack s
    7180                void ?{}( StackIter(T) & si, Stack(T) & s ) with( si ) {
    7281                        curr = &head( s );
     
    7786                } // post: curr = {e in s}
    7887
    79                 // make existing iterator active in Stack q
     88                // make existing iterator active in stack s
    8089                void over( StackIter(T) & si, Stack(T) & s ) with( si ) {
    8190                        curr = &head( s );
    8291                } // post: curr = {e in s}
    8392
    84                 bool ?>>?( StackIter(T) & si, T && tp ) with( si ) {
     93                bool ?|?( StackIter(T) & si, T && tp ) with( si ) {
    8594                        if ( curr ) {
    8695                                &tp = Curr( si );
  • libcfa/src/concurrency/invoke.h

    r42f6e07 r2b4daf2  
    189189                struct __monitor_group_t monitors;
    190190
     191                // used to put threads on user data structures
     192                struct {
     193                        struct $thread * next;
     194                        struct $thread * back;
     195                } seqable;
     196
    191197                struct {
    192198                        struct $thread * next;
     
    218224                }
    219225
     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
    220238                static inline void ?{}(__monitor_group_t & this) {
    221239                        (this.data){0p};
  • libcfa/src/concurrency/kernel/startup.cfa

    r42f6e07 r2b4daf2  
    118118
    119119extern size_t __page_size;
     120extern int __map_prot;
    120121
    121122//-----------------------------------------------------------------------------
     
    725726                }
    726727        #else
     728                __cfaabi_dbg_debug_do(
     729                        // pthread has no mechanism to create the guard page in user supplied stack.
     730                        if ( mprotect( stack, __page_size, __map_prot ) == -1 ) {
     731                                abort( "mprotect : internal error, mprotect failure, error(%d) %s.", errno, strerror( errno ) );
     732                        } // if
     733                );
    727734                free( stack );
    728735        #endif
  • libcfa/src/concurrency/locks.cfa

    r42f6e07 r2b4daf2  
    11#include "locks.hfa"
    22#include "kernel_private.hfa"
    3 #include <stdlib.h>
    4 #include <stdio.h>
    53
    64#include <kernel.hfa>
    75#include <stdlib.hfa>
    8 #include <thread.hfa>
    9 
    10 ///////////////////////////////////////////////////////////////////
    11 //// info_thread
    12 ///////////////////////////////////////////////////////////////////
    13 
     6
     7//-----------------------------------------------------------------------------
     8// info_thread
    149forall(dtype L | is_blocking_lock(L)) {
    15         void ?{}( info_thread(L) & this, $thread * t ) {
    16                 ((Seqable &) this){};
    17                 this.t = t;
    18                 this.lock = 0p;
    19                 this.listed = false;
    20         }
    21 
    22         void ?{}( info_thread(L) & this, $thread * t, uintptr_t info ) {
     10        struct info_thread {
     11                // used to put info_thread on a dl queue (aka sequence)
     12                inline Seqable;
     13
     14                // waiting thread
     15                struct $thread * t;
     16
     17                // shadow field
     18                uintptr_t info;
     19
     20                // lock that is passed to wait() (if one is passed)
     21                L * lock;
     22
     23                // true when signalled and false when timeout wakes thread
     24                bool signalled;
     25        };
     26
     27        void ?{}( info_thread(L) & this, $thread * t, uintptr_t info, L * l ) {
    2328                ((Seqable &) this){};
    2429                this.t = t;
    2530                this.info = info;
    26                 this.lock = 0p;
    27                 this.listed = false;
    28         }
    29 
    30         void ^?{}( info_thread(L) & this ){ }
    31 }
    32 
    33 ///////////////////////////////////////////////////////////////////
    34 //// Blocking Locks
    35 ///////////////////////////////////////////////////////////////////
    36 
     31                this.lock = l;
     32        }
     33
     34        void ^?{}( info_thread(L) & this ) {}
     35
     36        info_thread(L) *& Back( info_thread(L) * this ) {
     37                return (info_thread(L) *)Back( (Seqable *)this );
     38        }
     39
     40        info_thread(L) *& Next( info_thread(L) * this ) {
     41                return (info_thread(L) *)Next( (Colable *)this );
     42        }
     43}
     44
     45//-----------------------------------------------------------------------------
     46// Blocking Locks
    3747void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ) {
    3848        this.lock{};
     
    4656
    4757void ^?{}( blocking_lock & this ) {}
    48 void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}
     58void  ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}
    4959void ^?{}( single_acquisition_lock & this ) {}
    50 void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}
     60void  ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}
    5161void ^?{}( owner_lock & this ) {}
    52 void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };}
     62void  ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };}
    5363void ^?{}( multiple_acquisition_lock & this ) {}
    5464
    5565void lock( blocking_lock & this ) with( this ) {
    5666        lock( lock __cfaabi_dbg_ctx2 );
    57         if ( owner == active_thread() && !multi_acquisition) {
    58                 abort("A single acquisition lock holder attempted to reacquire the lock resulting in a deadlock.");
    59         } else if ( owner != 0p && owner != active_thread() ) {
    60                 append( blocked_threads, active_thread() );
     67        $thread * thrd = active_thread();
     68
     69        // single acquisition lock is held by current thread
     70        /* paranoid */ verifyf( owner != thrd || multi_acquisition, "Single acquisition lock holder (%p) attempted to reacquire the lock %p resulting in a deadlock.", owner, &this );
     71
     72        // lock is held by some other thread
     73        if ( owner != 0p && owner != thrd ) {
     74                addTail( blocked_threads, *thrd );
    6175                wait_count++;
    6276                unlock( lock );
    6377                park( );
    64         } else if ( owner == active_thread() && multi_acquisition ) {
     78        }
     79        // multi acquisition lock is held by current thread
     80        else if ( owner == thrd && multi_acquisition ) {
    6581                recursion_count++;
    6682                unlock( lock );
    67         } else {
    68                 owner = active_thread();
     83        }
     84        // lock isn't held
     85        else {
     86                owner = thrd;
    6987                recursion_count = 1;
    7088                unlock( lock );
     
    7593        bool ret = false;
    7694        lock( lock __cfaabi_dbg_ctx2 );
     95
     96        // lock isn't held
    7797        if ( owner == 0p ) {
    7898                owner = active_thread();
    7999                recursion_count = 1;
    80100                ret = true;
    81         } else if ( owner == active_thread() && multi_acquisition ) {
     101        }
     102        // multi acquisition lock is held by current thread
     103        else if ( owner == active_thread() && multi_acquisition ) {
    82104                recursion_count++;
    83105                ret = true;
    84106        }
     107
    85108        unlock( lock );
    86109        return ret;
    87110}
    88111
    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 
    97112void pop_and_set_new_owner( blocking_lock & this ) with( this ) {
    98         $thread * t = pop_head( blocked_threads );
     113        $thread * t = &dropHead( blocked_threads );
    99114        owner = t;
    100115        recursion_count = ( t ? 1 : 0 );
     
    105120void unlock( blocking_lock & this ) with( this ) {
    106121        lock( lock __cfaabi_dbg_ctx2 );
    107         unlock_error_check( this );
     122        /* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this );
     123        /* paranoid */ verifyf( owner == active_thread() || !strict_owner, "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this );
     124
     125        // if recursion count is zero release lock and set new owner if one is waiting
    108126        recursion_count--;
    109127        if ( recursion_count == 0 ) {
     
    125143}
    126144
    127 void add_( blocking_lock & this, $thread * t ) with( this ) {
    128     lock( lock __cfaabi_dbg_ctx2 );
     145void on_notify( blocking_lock & this, $thread * t ) with( this ) {
     146        lock( lock __cfaabi_dbg_ctx2 );
     147        // lock held
    129148        if ( owner != 0p ) {
    130                 append( blocked_threads, t );
     149                addTail( blocked_threads, *t );
    131150                wait_count++;
    132151                unlock( lock );
    133         } else {
     152        }
     153        // lock not held
     154        else {
    134155                owner = t;
    135156                recursion_count = 1;
     
    139160}
    140161
    141 void remove_( blocking_lock & this ) with( this ) {
    142     lock( lock __cfaabi_dbg_ctx2 );
    143         unlock_error_check( this );
     162void 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
    144167        pop_and_set_new_owner( this );
    145168        unlock( lock );
    146169}
    147170
    148 ///////////////////////////////////////////////////////////////////
    149 //// Overloaded routines for traits
    150 ///////////////////////////////////////////////////////////////////
    151 
    152 // This is temporary until an inheritance bug is fixed
    153 
    154 void lock( single_acquisition_lock & this ){ lock( (blocking_lock &)this ); }
    155 void unlock( single_acquisition_lock & this ){ unlock( (blocking_lock &)this ); }
    156 void add_( single_acquisition_lock & this, struct $thread * t ){ add_( (blocking_lock &)this, t ); }
    157 void remove_( single_acquisition_lock & this ){ remove_( (blocking_lock &)this ); }
    158 void set_recursion_count( single_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }
    159 size_t get_recursion_count( single_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }
    160 
    161 void lock( owner_lock & this ){ lock( (blocking_lock &)this ); }
    162 void unlock( owner_lock & this ){ unlock( (blocking_lock &)this ); }
    163 void add_( owner_lock & this, struct $thread * t ){ add_( (blocking_lock &)this, t ); }
    164 void remove_( owner_lock & this ){ remove_( (blocking_lock &)this ); }
    165 void set_recursion_count( owner_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }
    166 size_t get_recursion_count( owner_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }
    167 
    168 void lock( multiple_acquisition_lock & this ){ lock( (blocking_lock &)this ); }
    169 void unlock( multiple_acquisition_lock & this ){ unlock( (blocking_lock &)this ); }
    170 void add_( multiple_acquisition_lock & this, struct $thread * t ){ add_( (blocking_lock &)this, t ); }
    171 void remove_( multiple_acquisition_lock & this ){ remove_( (blocking_lock &)this ); }
    172 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }
     171//-----------------------------------------------------------------------------
     172// Overloaded routines for traits
     173// These routines are temporary until an inheritance bug is fixed
     174void   lock      ( single_acquisition_lock & this ) { lock   ( (blocking_lock &)this ); }
     175void   unlock    ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); }
     176void   on_wait   ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); }
     177void   on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }
     178void   set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); }
     179size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this ); }
     180
     181void   lock     ( owner_lock & this ) { lock   ( (blocking_lock &)this ); }
     182void   unlock   ( owner_lock & this ) { unlock ( (blocking_lock &)this ); }
     183void   on_wait  ( owner_lock & this ) { on_wait( (blocking_lock &)this ); }
     184void   on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }
     185void   set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); }
     186size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); }
     187
     188void   lock     ( multiple_acquisition_lock & this ) { lock   ( (blocking_lock &)this ); }
     189void   unlock   ( multiple_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); }
     190void   on_wait  ( multiple_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); }
     191void   on_notify( multiple_acquisition_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); }
     192void   set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }
    173193size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }
    174194
    175 ///////////////////////////////////////////////////////////////////
    176 //// condition variable
    177 ///////////////////////////////////////////////////////////////////
    178 
     195//-----------------------------------------------------------------------------
     196// alarm node wrapper
    179197forall(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 ) { }
    180211
    181212        void timeout_handler ( alarm_node_wrap(L) & this ) with( this ) {
    182         // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin.
    183             lock( cond->lock __cfaabi_dbg_ctx2 );
    184            
    185             if ( i->listed ) {                  // is thread on queue
    186                 cond->last_thread = i;          // REMOVE THIS AFTER DEBUG
    187                         remove( cond->blocked_threads, *i );             //remove this thread O(1)
     213                // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin.
     214                lock( cond->lock __cfaabi_dbg_ctx2 );
     215
     216                // this check is necessary to avoid a race condition since this timeout handler
     217                //      may still be called after a thread has been removed from the queue but
     218                //      before the alarm is unregistered
     219                if ( listed(i) ) {      // is thread on queue
     220                        i->signalled = false;
     221                        // remove this thread O(1)
     222                        remove( cond->blocked_threads, *i );
    188223                        cond->count--;
    189                         if( !i->lock ) {
     224                        if( i->lock ) {
     225                                // call lock's on_notify if a lock was passed
     226                                on_notify(*i->lock, i->t);
     227                        } else {
     228                                // otherwise wake thread
    190229                                unpark( i->t );
    191                 } else {
    192                         add_(*i->lock, i->t);                   // call lock's add_
    193                 }
    194             }
    195             unlock( cond->lock );
    196         }
    197 
     230                        }
     231                }
     232                unlock( cond->lock );
     233        }
     234
     235        // this casts the alarm node to our wrapped type since we used type erasure
    198236        void alarm_node_wrap_cast( alarm_node_t & a ) { timeout_handler( (alarm_node_wrap(L) &)a ); }
     237}
     238
     239//-----------------------------------------------------------------------------
     240// condition variable
     241forall(dtype L | is_blocking_lock(L)) {
    199242
    200243        void ?{}( condition_variable(L) & this ){
     
    202245                this.blocked_threads{};
    203246                this.count = 0;
    204                 this.last_thread = 0p; // REMOVE AFTER DEBUG
    205247        }
    206248
    207249        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 ) { }
    214250
    215251        void process_popped( condition_variable(L) & this, info_thread(L) & popped ) with( this ) {
    216252                if(&popped != 0p) {
    217                         popped.listed = false;
     253                        popped.signalled = true;
    218254                        count--;
    219255                        if (popped.lock) {
    220                                 add_(*popped.lock, popped.t);
     256                                // if lock passed call on_notify
     257                                on_notify(*popped.lock, popped.t);
    221258                        } else {
     259                                // otherwise wake thread
    222260                                unpark(popped.t);
    223261                        }
     
    252290
    253291        size_t queue_and_get_recursion( condition_variable(L) & this, info_thread(L) * i ) with(this) {
     292                // add info_thread to waiting queue
    254293                addTail( blocked_threads, *i );
    255294                count++;
    256                 i->listed = true;
    257295                size_t recursion_count = 0;
    258296                if (i->lock) {
    259                         i->t->link.next = 1p;
     297                        // if lock was passed get recursion count to reset to after waking thread
    260298                        recursion_count = get_recursion_count(*i->lock);
    261                         remove_( *i->lock );
     299                        on_wait( *i->lock );
    262300                }
    263301                return recursion_count;
     
    269307                size_t recursion_count = queue_and_get_recursion(this, &i);
    270308                unlock( lock );
    271                 park( ); // blocks here
    272                 if (i.lock) set_recursion_count(*i.lock, recursion_count); // resets recursion count here after waking
    273         }
     309
     310                // blocks here
     311                park( );
     312
     313                // resets recursion count here after waking
     314                if (i.lock) set_recursion_count(*i.lock, recursion_count);
     315        }
     316
     317        #define WAIT( u, l ) \
     318                info_thread( L ) i = { active_thread(), u, l }; \
     319                queue_info_thread( this, i );
    274320
    275321        // helper for wait()'s' with a timeout
     
    277323                lock( lock __cfaabi_dbg_ctx2 );
    278324                size_t recursion_count = queue_and_get_recursion(this, &info);
    279                 alarm_node_wrap(L) node_wrap = { t, 0`s, alarm_node_wrap_cast };
    280                 node_wrap.cond = &this;
    281                 node_wrap.i = &info;
     325                alarm_node_wrap(L) node_wrap = { t, 0`s, alarm_node_wrap_cast, &this, &info };
    282326                register_self( &node_wrap.alarm_node );
    283327                unlock( lock );
     328
     329                // blocks here
    284330                park();
     331
     332                // unregisters alarm so it doesn't go off if this happens first
    285333                unregister_self( &node_wrap.alarm_node );
     334
     335                // resets recursion count here after waking
    286336                if (info.lock) set_recursion_count(*info.lock, recursion_count);
    287337        }
    288338
    289         void wait( condition_variable(L) & this ) with(this) {
    290                 info_thread( L ) i = { active_thread() };
    291                 queue_info_thread( this, i );
    292         }
    293 
    294         void wait( condition_variable(L) & this, uintptr_t info ) with(this) {
    295                 info_thread( L ) i = { active_thread(), info };
    296                 queue_info_thread( this, i );
    297         }
    298        
    299         void wait( condition_variable(L) & this, Duration duration ) with(this) {
    300                 info_thread( L ) i = { active_thread() };
    301                 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
    302         }
    303 
    304         void wait( condition_variable(L) & this, uintptr_t info, Duration duration ) with(this) {
    305                 info_thread( L ) i = { active_thread(), info };
    306                 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
    307         }
    308 
    309         void wait( condition_variable(L) & this, Time time ) with(this) {
    310                 info_thread( L ) i = { active_thread() };
    311                 queue_info_thread_timeout(this, i, time);
    312         }
    313 
    314         void wait( condition_variable(L) & this, uintptr_t info, Time time ) with(this) {
    315                 info_thread( L ) i = { active_thread(), info };
    316                 queue_info_thread_timeout(this, i, time);
    317         }
    318 
    319         void wait( condition_variable(L) & this, L & l ) with(this) {
    320                 info_thread(L) i = { active_thread() };
    321                 i.lock = &l;
    322                 queue_info_thread( this, i );
    323         }
    324 
    325         void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) {
    326                 info_thread(L) i = { active_thread(), info };
    327                 i.lock = &l;
    328                 queue_info_thread( this, i );
    329         }
    330        
    331         void wait( condition_variable(L) & this, L & l, Duration duration ) with(this) {
    332                 info_thread(L) i = { active_thread() };
    333                 i.lock = &l;
    334                 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
    335         }
    336        
    337         void wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) {
    338                 info_thread(L) i = { active_thread(), info };
    339                 i.lock = &l;
    340                 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
    341         }
    342        
    343         void wait( condition_variable(L) & this, L & l, Time time ) with(this) {
    344                 info_thread(L) i = { active_thread() };
    345                 i.lock = &l;
    346                 queue_info_thread_timeout(this, i, time );
    347         }
    348        
    349         void wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) {
    350                 info_thread(L) i = { active_thread(), info };
    351                 i.lock = &l;
    352                 queue_info_thread_timeout(this, i, time );
    353         }
    354 }
     339        #define WAIT_TIME( u, l, t ) \
     340                info_thread( L ) i = { active_thread(), u, l }; \
     341                queue_info_thread_timeout(this, i, t ); \
     342                return i.signalled;
     343
     344        void wait( condition_variable(L) & this                        ) with(this) { WAIT( 0, 0p    ) }
     345        void wait( condition_variable(L) & this, uintptr_t info        ) with(this) { WAIT( info, 0p ) }
     346        void wait( condition_variable(L) & this, L & l                 ) with(this) { WAIT( 0, &l    ) }
     347        void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) { WAIT( info, &l ) }
     348
     349        bool wait( condition_variable(L) & this, Duration duration                        ) with(this) { WAIT_TIME( 0   , 0p , __kernel_get_time() + duration ) }
     350        bool wait( condition_variable(L) & this, uintptr_t info, Duration duration        ) with(this) { WAIT_TIME( info, 0p , __kernel_get_time() + duration ) }
     351        bool wait( condition_variable(L) & this, Time time                                ) with(this) { WAIT_TIME( 0   , 0p , time ) }
     352        bool wait( condition_variable(L) & this, uintptr_t info, Time time                ) with(this) { WAIT_TIME( info, 0p , time ) }
     353        bool wait( condition_variable(L) & this, L & l, Duration duration                 ) with(this) { WAIT_TIME( 0   , &l , __kernel_get_time() + duration ) }
     354        bool wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) { WAIT_TIME( info, &l , __kernel_get_time() + duration ) }
     355        bool wait( condition_variable(L) & this, L & l, Time time                         ) with(this) { WAIT_TIME( 0   , &l , time ) }
     356        bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time         ) with(this) { WAIT_TIME( info, &l , time ) }
     357}
  • libcfa/src/concurrency/locks.hfa

    r42f6e07 r2b4daf2  
    33#include <stdbool.h>
    44
    5 #include "bits/algorithm.hfa"
    65#include "bits/locks.hfa"
    76#include "bits/sequence.hfa"
    8 #include "bits/containers.hfa"
    97
    108#include "invoke.h"
     
    1210#include "time_t.hfa"
    1311#include "time.hfa"
    14 #include <sys/time.h>
    15 #include "alarm.hfa"
    1612
    17 ///////////////////////////////////////////////////////////////////
    18 //// is_blocking_lock
    19 ///////////////////////////////////////////////////////////////////
     13//-----------------------------------------------------------------------------
     14// is_blocking_lock
     15trait is_blocking_lock(dtype L | sized(L)) {
     16        // For synchronization locks to use when acquiring
     17        void on_notify( L &, struct $thread * );
    2018
    21 trait is_blocking_lock(dtype L | sized(L)) {
    22         void add_( L &, struct $thread * );             // For synchronization locks to use when acquiring
    23         void remove_( L & );    // For synchronization locks to use when releasing
    24         size_t get_recursion_count( L & ); // to get recursion count for cond lock to reset after waking
    25         void set_recursion_count( L &, size_t recursion ); // to set recursion count after getting signalled;
     19        // For synchronization locks to use when releasing
     20        void on_wait( L & );
     21
     22        // to get recursion count for cond lock to reset after waking
     23        size_t get_recursion_count( L & );
     24
     25        // to set recursion count after getting signalled;
     26        void set_recursion_count( L &, size_t recursion );
    2627};
    2728
    28 ///////////////////////////////////////////////////////////////////
    29 //// info_thread
    30 ///////////////////////////////////////////////////////////////////
     29//-----------------------------------------------------------------------------
     30// info_thread
     31// the info thread is a wrapper around a thread used
     32// to store extra data for use in the condition variable
     33forall(dtype L | is_blocking_lock(L)) {
     34        struct info_thread;
    3135
    32 forall(dtype L | is_blocking_lock(L)) {
    33         struct info_thread {
    34                 inline Seqable;
    35                 struct $thread * t;
    36                 uintptr_t info;
    37                 L * lock;
    38                 bool listed;                                    // true if info_thread is on queue, false otherwise;
    39         };
    40 
    41 
    42         void ?{}( info_thread(L) & this, $thread * t );
    43         void ?{}( info_thread(L) & this, $thread * t, uintptr_t info );
    44         void ^?{}( info_thread(L) & this );
     36        // for use by sequence
     37        info_thread(L) *& Back( info_thread(L) * this );
     38        info_thread(L) *& Next( info_thread(L) * this );
    4539}
    4640
    47 ///////////////////////////////////////////////////////////////////
    48 //// Blocking Locks
    49 ///////////////////////////////////////////////////////////////////
    50 
    51 // struct lock_thread {
    52 //      struct $thread * t;
    53 //      lock_thread * next;
    54 // };
    55 
    56 // void ?{}( lock_thread & this, struct $thread * thd );
    57 // void ^?{}( lock_thread & this );
    58 
    59 // lock_thread *& get_next( lock_thread & );
    60 
     41//-----------------------------------------------------------------------------
     42// Blocking Locks
    6143struct blocking_lock {
    6244        // Spin lock used for mutual exclusion
     
    6446
    6547        // List of blocked threads
    66         __queue_t( $thread ) blocked_threads;
     48        Sequence( $thread ) blocked_threads;
    6749
    6850        // Count of current blocked threads
     
    9476};
    9577
    96 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner );
     78void  ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner );
    9779void ^?{}( blocking_lock & this );
    9880
    99 void ?{}( single_acquisition_lock & this );
     81void  ?{}( single_acquisition_lock & this );
    10082void ^?{}( single_acquisition_lock & this );
    10183
    102 void ?{}( owner_lock & this );
     84void  ?{}( owner_lock & this );
    10385void ^?{}( owner_lock & this );
    10486
    105 void ?{}( multiple_acquisition_lock & this );
     87void  ?{}( multiple_acquisition_lock & this );
    10688void ^?{}( multiple_acquisition_lock & this );
    10789
     
    10991bool try_lock( blocking_lock & this );
    11092void unlock( blocking_lock & this );
    111 void add_( blocking_lock & this, struct $thread * t );
    112 void remove_( blocking_lock & this );
     93void on_notify( blocking_lock & this, struct $thread * t );
     94void on_wait( blocking_lock & this );
    11395size_t wait_count( blocking_lock & this );
    11496void set_recursion_count( blocking_lock & this, size_t recursion );
     
    11799void lock( single_acquisition_lock & this );
    118100void unlock( single_acquisition_lock & this );
    119 void add_( single_acquisition_lock & this, struct $thread * t );
    120 void remove_( single_acquisition_lock & this );
     101void on_notify( single_acquisition_lock & this, struct $thread * t );
     102void on_wait( single_acquisition_lock & this );
    121103void set_recursion_count( single_acquisition_lock & this, size_t recursion );
    122104size_t get_recursion_count( single_acquisition_lock & this );
     
    124106void lock( owner_lock & this );
    125107void unlock( owner_lock & this );
    126 void add_( owner_lock & this, struct $thread * t );
    127 void remove_( owner_lock & this );
     108void on_notify( owner_lock & this, struct $thread * t );
     109void on_wait( owner_lock & this );
    128110void set_recursion_count( owner_lock & this, size_t recursion );
    129111size_t get_recursion_count( owner_lock & this );
     
    131113void lock( multiple_acquisition_lock & this );
    132114void unlock( multiple_acquisition_lock & this );
    133 void add_( multiple_acquisition_lock & this, struct $thread * t );
    134 void remove_( multiple_acquisition_lock & this );
     115void on_notify( multiple_acquisition_lock & this, struct $thread * t );
     116void on_wait( multiple_acquisition_lock & this );
    135117void set_recursion_count( multiple_acquisition_lock & this, size_t recursion );
    136118size_t get_recursion_count( multiple_acquisition_lock & this );
    137119
    138 ///////////////////////////////////////////////////////////////////
    139 //// Synchronization Locks
    140 ///////////////////////////////////////////////////////////////////
     120//-----------------------------------------------------------------------------
     121// Synchronization Locks
    141122forall(dtype L | is_blocking_lock(L)) {
    142123        struct condition_variable {
    143124                // Spin lock used for mutual exclusion
    144125                __spinlock_t lock;
    145 
    146                 info_thread(L) * last_thread;
    147126
    148127                // List of blocked threads
     
    153132        };
    154133
    155         void ?{}( condition_variable(L) & this );
     134        void  ?{}( condition_variable(L) & this );
    156135        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 );
    172136
    173137        bool notify_one( condition_variable(L) & this );
     
    176140        uintptr_t front( condition_variable(L) & this );
    177141
    178         bool empty( condition_variable(L) & this );
    179         int counter( condition_variable(L) & this );
     142        bool empty  ( condition_variable(L) & this );
     143        int  counter( condition_variable(L) & this );
    180144
    181         // TODO: look into changing timout routines to return bool showing if signalled or woken by kernel
    182145        void wait( condition_variable(L) & this );
    183146        void wait( condition_variable(L) & this, uintptr_t info );
    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 );
     147        bool wait( condition_variable(L) & this, Duration duration );
     148        bool wait( condition_variable(L) & this, uintptr_t info, Duration duration );
     149        bool wait( condition_variable(L) & this, Time time );
     150        bool wait( condition_variable(L) & this, uintptr_t info, Time time );
    188151
    189152        void wait( condition_variable(L) & this, L & l );
    190153        void wait( condition_variable(L) & this, L & l, uintptr_t info );
    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 );
     154        bool wait( condition_variable(L) & this, L & l, Duration duration );
     155        bool wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration );
     156        bool wait( condition_variable(L) & this, L & l, Time time );
     157        bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time );
    195158}
  • libcfa/src/concurrency/monitor.hfa

    r42f6e07 r2b4daf2  
    132132
    133133              void wait        ( condition & this, uintptr_t user_info = 0 );
     134static inline bool is_empty    ( condition & this ) { return this.blocked.head == 1p; }
    134135              bool signal      ( condition & this );
    135136              bool signal_block( condition & this );
    136 static inline bool is_empty    ( condition & this ) { return this.blocked.head == 1p; }
     137static inline bool signal_all  ( condition & this ) { bool ret = false; while(!is_empty(this)) { ret = signal(this) || ret; } return ret; }
    137138         uintptr_t front       ( condition & this );
    138139
  • libcfa/src/concurrency/thread.cfa

    r42f6e07 r2b4daf2  
    4343                canary = 0x0D15EA5E0D15EA5Ep;
    4444        #endif
     45
     46        seqable.next = 0p;
     47        seqable.back = 0p;
    4548
    4649        node.next = 0p;
     
    130133
    131134        this_thrd->context.[SP, FP] = this_thrd->self_cor.context.[SP, FP];
    132         verify( this_thrd->context.SP );
     135        /* paranoid */ verify( this_thrd->context.SP );
    133136
    134137        __schedule_thread( this_thrd );
  • libcfa/src/heap.cfa

    r42f6e07 r2b4daf2  
    1010// Created On       : Tue Dec 19 21:58:35 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Dec 15 21:37:54 2020
    13 // Update Count     : 1013
     12// Last Modified On : Wed Dec 16 12:28:25 2020
     13// Update Count     : 1023
    1414//
    1515
     
    438438        header = headerAddr( addr );
    439439
    440   if ( unlikely( heapEnd < addr ) ) {                                   // mmapped ?
     440  if ( unlikely( addr < heapBegin || heapEnd < addr ) ) { // mmapped ?
    441441                fakeHeader( header, alignment );
    442442                size = header->kind.real.blockSize & -3;                // mmap size
     
    445445
    446446        #ifdef __CFA_DEBUG__
    447         checkHeader( addr < heapBegin, name, addr );            // bad low address ?
     447        checkHeader( header < (HeapManager.Storage.Header *)heapBegin, name, addr ); // bad low address ?
    448448        #endif // __CFA_DEBUG__
    449449
     
    484484#endif // __CFA_DEBUG__
    485485
     486
    486487#define NO_MEMORY_MSG "insufficient heap memory available for allocating %zd new bytes."
    487488
    488 #include <unistd.h>
    489489static inline void * extend( size_t size ) with( heapManager ) {
    490490        lock( extlock __cfaabi_dbg_ctx2 );
     
    511511                #ifdef __CFA_DEBUG__
    512512                // Set new memory to garbage so subsequent uninitialized usages might fail.
    513                 memset( (char *)heapEnd + heapRemaining, '\hde', increase );
     513                memset( (char *)heapEnd + heapRemaining, '\xde', increase );
    514514                //Memset( (char *)heapEnd + heapRemaining, increase );
    515515                #endif // __CFA_DEBUG__
     
    586586                #ifdef __CFA_DEBUG__
    587587                // Set new memory to garbage so subsequent uninitialized usages might fail.
    588                 memset( block, '\hde', tsize );
     588                memset( block, '\xde', tsize );
    589589                //Memset( block, tsize );
    590590                #endif // __CFA_DEBUG__
     
    634634                #ifdef __CFA_DEBUG__
    635635                // Set free memory to garbage so subsequent usages might fail.
    636                 memset( ((HeapManager.Storage *)header)->data, '\hde', freeElem->blockSize - sizeof( HeapManager.Storage ) );
     636                memset( ((HeapManager.Storage *)header)->data, '\xde', freeElem->blockSize - sizeof( HeapManager.Storage ) );
    637637                //Memset( ((HeapManager.Storage *)header)->data, freeElem->blockSize - sizeof( HeapManager.Storage ) );
    638638                #endif // __CFA_DEBUG__
     
    10291029        } // cmemalign
    10301030
     1031
    10311032        // Same as memalign(), but ISO/IEC 2011 C11 Section 7.22.2 states: the value of size shall be an integral multiple
    10321033    // of alignment. This requirement is universally ignored.
     
    10451046                return 0;
    10461047        } // posix_memalign
     1048
    10471049
    10481050        // Allocates size bytes and returns a pointer to the allocated memory. The memory address shall be a multiple of the
  • libcfa/src/memory.cfa

    r42f6e07 r2b4daf2  
    6666forall(dtype T | sized(T), ttype Args | { void ?{}(T&, Args); })
    6767void ?{}(counter_ptr(T) & this, Args args) {
    68         this.data = new(args);
     68        this.data = (counter_data(T)*)new(args);
    6969}
    7070
     
    126126forall(dtype T | sized(T), ttype Args | { void ?{}(T &, Args); })
    127127void ?{}(unique_ptr(T) & this, Args args) {
    128         this.data = new(args);
     128        this.data = (T *)new(args);
    129129}
    130130
  • libcfa/src/parseargs.cfa

    r42f6e07 r2b4daf2  
    105105                                        if(opt == options[i].short_name) {
    106106                                                const char * arg = optarg ? optarg : "";
     107                                                if( arg[0] == '=' ) { arg++; }
    107108                                                bool success = options[i].parse( arg, options[i].variable );
    108109                                                if(success) continue NEXT_ARG;
     
    185186}
    186187
     188bool 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
    187202bool parse_settrue (const char *, bool & value ) {
    188203        value = true;
  • libcfa/src/parseargs.hfa

    r42f6e07 r2b4daf2  
    3737void print_args_usage(int argc, char * argv[], cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn));
    3838
    39 bool parse_yesno   (const char *, bool & );
    40 bool parse_settrue (const char *, bool & );
    41 bool parse_setfalse(const char *, bool & );
     39bool parse_yesno    (const char *, bool & );
     40bool parse_truefalse(const char *, bool & );
     41bool parse_settrue  (const char *, bool & );
     42bool parse_setfalse (const char *, bool & );
    4243
    4344bool parse(const char *, const char * & );
  • libcfa/src/stdlib.hfa

    r42f6e07 r2b4daf2  
    267267static inline forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } )
    268268T * new( TT p ) {
    269         return &(*malloc()){ p };                                                       // run constructor
     269        return &(*(T *)malloc()){ p };                                                  // run constructor
    270270} // new
    271271
  • longrun_tests/Makefile.am

    r42f6e07 r2b4daf2  
    4444        -DTEST_$(shell cat .type | tr a-z A-Z)
    4545
    46 TESTS = block coroutine create disjoint enter enter3 processor stack wait yield
     46TESTS = block coroutine create disjoint enter enter3 locks processor stack wait yield
    4747
    4848# .INTERMEDIATE: $(TESTS)
  • src/AST/Convert.cpp

    r42f6e07 r2b4daf2  
    205205                ftype->parameters = get<DeclarationWithType>().acceptL(node->params);
    206206
    207                 ftype->forall = get<TypeDecl>().acceptL( node->type->forall );
     207                ftype->forall = get<TypeDecl>().acceptL( node->type_params );
     208                if (!node->assertions.empty()) {
     209                        assert(!ftype->forall.empty());
     210                        // find somewhere to place assertions back, for convenience it is the last slot
     211                        ftype->forall.back()->assertions = get<DeclarationWithType>().acceptL(node->assertions);
     212                }
    208213
    209214                visitType(node->type, ftype);
     
    602607
    603608                for (decltype(src->begin()) src_i = src->begin(); src_i != src->end(); src_i++) {
    604                         rslt->add( src_i->first,
     609                        rslt->add( src_i->first.typeString(),
    605610                                   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) );
    611611                }
    612612
     
    12121212                // ty->returnVals = get<DeclarationWithType>().acceptL( node->returns );
    12131213                // ty->parameters = get<DeclarationWithType>().acceptL( node->params );
    1214                 ty->forall = get<TypeDecl>().acceptL( node->forall );
     1214
     1215                auto types = get<TypeInstType>().acceptL( node->forall );
     1216                for (auto t : types) {
     1217                        auto newT = new TypeDecl(*t->baseType);
     1218                        newT->name = t->name; // converted by typeString()
     1219                        for (auto asst : newT->assertions) delete asst;
     1220                        newT->assertions.clear();
     1221                        ty->forall.push_back(newT);
     1222                }
     1223                auto assts = get<VariableExpr>().acceptL( node->assertions );
     1224                if (!assts.empty()) {
     1225                        assert(!types.empty());
     1226                        for (auto asst : assts) {
     1227                                auto newDecl = new ObjectDecl(*strict_dynamic_cast<ObjectDecl*>(asst->var));
     1228                                delete newDecl->type;
     1229                                newDecl->type = asst->result->clone();
     1230                                newDecl->storageClasses.is_extern = true; // hack
     1231                                ty->forall.back()->assertions.push_back(newDecl);
     1232                        }
     1233                }
     1234
    12151235                return visitType( node, ty );
    12161236        }
     
    12991319                        ty = new TypeInstType{
    13001320                                cv( node ),
    1301                                 node->name,
     1321                                node->typeString(),
    13021322                                get<TypeDecl>().accept1( node->base ),
    13031323                                get<Attribute>().acceptL( node->attributes )
     
    13061326                        ty = new TypeInstType{
    13071327                                cv( node ),
    1308                                 node->name,
     1328                                node->typeString(),
    13091329                                node->kind == ast::TypeDecl::Ftype,
    13101330                                get<Attribute>().acceptL( node->attributes )
     
    14311451        /// at conversion stage, all created nodes are guaranteed to be unique, therefore
    14321452        /// const_casting out of smart pointers is permitted.
    1433         std::unordered_map< const BaseSyntaxNode *, ast::ptr<ast::Node> > cache = {};
     1453        std::unordered_map< const BaseSyntaxNode *, ast::readonly<ast::Node> > cache = {};
    14341454
    14351455        // Local Utilities:
     
    15651585                // can function type have attributes? seems not to be the case.
    15661586                // visitType(old->type, ftype);
     1587
     1588                // collect assertions and put directly in FunctionDecl
     1589                std::vector<ast::ptr<ast::DeclWithType>> assertions;
     1590                for (auto & param: forall) {
     1591                        for (auto & asst: param->assertions) {
     1592                                assertf(asst->unique(), "newly converted decl must be unique");
     1593                                assertions.emplace_back(asst);
     1594                        }
     1595                        auto mut = param.get_and_mutate();
     1596                        assertf(mut == param, "newly converted decl must be unique");
     1597                        mut->assertions.clear();
     1598                }
    15671599
    15681600                auto decl = new ast::FunctionDecl{
     
    15841616                cache.emplace( old, decl );
    15851617
     1618                decl->assertions = std::move(assertions);
    15861619                decl->withExprs = GET_ACCEPT_V(withExprs, Expr);
    15871620                decl->stmts = GET_ACCEPT_1(statements, CompoundStmt);
     
    20662099        }
    20672100
     2101        // TypeSubstitution shouldn't exist yet in old.
    20682102        ast::TypeSubstitution * convertTypeSubstitution(const TypeSubstitution * old) {
    2069 
     2103               
    20702104                if (!old) return nullptr;
    2071 
     2105                if (old->empty()) return nullptr;
     2106                assert(false);
     2107
     2108                /*
    20722109                ast::TypeSubstitution *rslt = new ast::TypeSubstitution();
    20732110
     
    20772114                }
    20782115
    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 
    20842116                return rslt;
     2117                */
    20852118        }
    20862119
     
    26102643                        ty->params.emplace_back(v->get_type());
    26112644                }
    2612                 ty->forall = GET_ACCEPT_V( forall, TypeDecl );
     2645                // xxx - when will this be non-null?
     2646                // will have to create dangling (no-owner) decls to be pointed to
     2647                auto foralls = GET_ACCEPT_V( forall, TypeDecl );
     2648
     2649                for (auto & param : foralls) {
     2650                        ty->forall.emplace_back(new ast::TypeInstType(param->name, param));
     2651                        for (auto asst : param->assertions) {
     2652                                ty->assertions.emplace_back(new ast::VariableExpr({}, asst));
     2653                        }
     2654                }
    26132655                visitType( old, ty );
    26142656        }
  • src/AST/Decl.cpp

    r42f6e07 r2b4daf2  
    5050
    5151FunctionDecl::FunctionDecl( const CodeLocation & loc, const std::string & name,
    52                 std::vector<ptr<TypeDecl>>&& forall,
    53                 std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
    54                 CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,
    55                 std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)
    56         : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
    57           stmts( stmts ) {
    58                   FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs));
    59                   for (auto & param : this->params) {
    60                           ftype->params.emplace_back(param->get_type());
    61                   }
    62                   for (auto & ret : this->returns) {
    63                           ftype->returns.emplace_back(ret->get_type());
    64                   }
    65                   ftype->forall = std::move(forall);
    66                   this->type = ftype;
    67           }
     52        std::vector<ptr<TypeDecl>>&& forall,
     53        std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
     54        CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,
     55        std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)
     56: DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
     57        type_params(std::move(forall)), stmts( stmts ) {
     58        FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs));
     59        for (auto & param : this->params) {
     60                ftype->params.emplace_back(param->get_type());
     61        }
     62        for (auto & ret : this->returns) {
     63                ftype->returns.emplace_back(ret->get_type());
     64        }
     65        for (auto & tp : this->type_params) {
     66                ftype->forall.emplace_back(new TypeInstType(tp->name, tp));
     67        }
     68        this->type = ftype;
     69}
    6870
    6971
  • src/AST/Decl.hpp

    r42f6e07 r2b4daf2  
    127127        std::vector<ptr<DeclWithType>> params;
    128128        std::vector<ptr<DeclWithType>> returns;
     129        std::vector<ptr<TypeDecl>> type_params;
     130        std::vector<ptr<DeclWithType>> assertions;
    129131        // declared type, derived from parameter declarations
    130132        ptr<FunctionType> type;
    131133        ptr<CompoundStmt> stmts;
    132134        std::vector< ptr<Expr> > withExprs;
     135
    133136
    134137        FunctionDecl( const CodeLocation & loc, const std::string & name, std::vector<ptr<TypeDecl>>&& forall,
  • src/AST/Expr.cpp

    r42f6e07 r2b4daf2  
    206206        assert( aggregate->result );
    207207
    208         // Deep copy on result type avoids mutation on transitively multiply referenced object.
    209         //
    210         // Example, adapted from parts of builtins and bootloader:
    211         //
    212         // forall(dtype T)
    213         // struct __Destructor {
    214         //   T * object;
    215         //   void (*dtor)(T *);
    216         // };
    217         //
    218         // forall(dtype S)
    219         // void foo(__Destructor(S) &d) {
    220         //   if (d.dtor) {  // here
    221         //   }
    222         // }
    223         //
    224         // Let e be the "d.dtor" guard espression, which is MemberExpr after resolve.  Let d be the
    225         // declaration of member __Destructor.dtor (an ObjectDecl), as accessed via the top-level
    226         // declaration of __Destructor.  Consider the types e.result and d.type.  In the old AST, one
    227         // is a clone of the other.  Ordinary new-AST use would set them up as a multiply-referenced
    228         // object.
    229         //
    230         // e.result: PointerType
    231         // .base: FunctionType
    232         // .params.front(): ObjectDecl, the anonymous parameter of type T*
    233         // .type: PointerType
    234         // .base: TypeInstType
    235         // let x = that
    236         // let y = similar, except start from d.type
    237         //
    238         // Consider two code lines down, genericSubstitution(...).apply(result).
    239         //
    240         // Applying this chosen-candidate's type substitution means modifying x, substituting
    241         // S for T.  This mutation should affect x and not y.
    242 
    243         result = deepCopy(mem->get_type());
     208        result = mem->get_type();
    244209
    245210        // substitute aggregate generic parameters into member type
  • src/AST/Node.hpp

    r42f6e07 r2b4daf2  
    229229        const node_t & operator* () const { _check(); return *node; }
    230230        explicit operator bool() const { _check(); return node; }
    231         operator const node_t * () const { _check(); return node; }
     231        operator const node_t * () const & { _check(); return node; }
     232        operator const node_t * () && = delete;
    232233
    233234        const node_t * release() {
  • src/AST/Pass.hpp

    r42f6e07 r2b4daf2  
    3434
    3535#include "AST/SymbolTable.hpp"
    36 
    37 #include "AST/ForallSubstitutionTable.hpp"
    3836
    3937// Private prelude header, needed for some of the magic tricks this class pulls off
     
    6664// | WithVisitorRef        - provides an pointer to the templated visitor wrapper
    6765// | WithSymbolTable       - provides symbol table functionality
    68 // | WithForallSubstitutor - maintains links between TypeInstType and TypeDecl under mutation
    6966//
    7067// Other Special Members:
     
    258255        container_t< ptr<node_t> > call_accept( const container_t< ptr<node_t> > & container );
    259256
    260         /// Mutate forall-list, accounting for presence of type substitution map
    261         template<typename node_t>
    262         void mutate_forall( const node_t *& );
    263 
    264257public:
    265258        /// Logic to call the accept and mutate the parent if needed, delegates call to accept
     
    398391};
    399392
    400 /// Use when the templated visitor needs to keep TypeInstType instances properly linked to TypeDecl
    401 struct WithForallSubstitutor {
    402         ForallSubstitutionTable subs;
    403 };
    404 
    405393}
    406394
  • src/AST/Pass.impl.hpp

    r42f6e07 r2b4daf2  
    367367        }
    368368
    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         }
    385369}
    386370
     
    504488                        __pass::symtab::addId( core, 0, func );
    505489                        VISIT(
    506                                 // parameter declarations are now directly here
     490                                // parameter declarations
    507491                                maybe_accept( node, &FunctionDecl::params );
    508492                                maybe_accept( node, &FunctionDecl::returns );
    509                                 // foralls are still in function type
    510                                 maybe_accept( node, &FunctionDecl::type );
     493                                // type params and assertions
     494                                maybe_accept( node, &FunctionDecl::type_params );
     495                                maybe_accept( node, &FunctionDecl::assertions );
    511496                                // First remember that we are now within a function.
    512497                                ValueGuard< bool > oldInFunction( inFunction );
     
    17581743
    17591744        VISIT({
    1760                 guard_forall_subs forall_guard { *this, node };
    1761                 mutate_forall( node );
     1745                // guard_forall_subs forall_guard { *this, node };
     1746                // mutate_forall( node );
     1747                maybe_accept( node, &FunctionType::assertions );
    17621748                maybe_accept( node, &FunctionType::returns );
    17631749                maybe_accept( node, &FunctionType::params  );
     
    19811967                {
    19821968                        bool mutated = false;
    1983                         std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;
     1969                        std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map;
    19841970                        for ( const auto & p : node->typeEnv ) {
    19851971                                guard_symtab guard { *this };
     
    19941980                        }
    19951981                }
    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                 }
    20121982        )
    20131983
  • src/AST/Pass.proto.hpp

    r42f6e07 r2b4daf2  
    413413                static inline auto leave( core_t &, long, const ast::FunctionType * ) {}
    414414
    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 
    424415                // Replaces a TypeInstType's base TypeDecl according to the table
    425416                template<typename core_t>
  • src/AST/Print.cpp

    r42f6e07 r2b4daf2  
    155155        }
    156156
     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
    157166        void print( const std::vector<ptr<Attribute>> & attrs ) {
    158167                if ( attrs.empty() ) return;
     
    206215        void preprint( const ast::NamedTypeDecl * node ) {
    207216                if ( ! node->name.empty() ) {
    208                         if( deterministic_output && isUnboundType(node->name) ) os << "[unbound]:";
    209                         else os << node->name << ": ";
     217                        os << node->name << ": ";
    210218                }
    211219
     
    261269        void preprint( const ast::FunctionType * node ) {
    262270                print( node->forall );
     271                print( node->assertions );
    263272                print( node->qualifiers );
    264273        }
     
    13751384        virtual const ast::Type * visit( const ast::TypeInstType * node ) override final {
    13761385                preprint( node );
    1377                 const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->name;
     1386                const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->typeString();
    13781387                os << "instance of type " << _name
    13791388                   << " (" << (node->kind == ast::TypeDecl::Ftype ? "" : "not ") << "function type)";
     
    15021511                os << indent << "Types:" << endl;
    15031512                for ( const auto& i : *node ) {
    1504                         os << indent+1 << i.first << " -> ";
     1513                        os << indent+1 << i.first.typeString() << " -> ";
    15051514                        indent += 2;
    15061515                        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 );
    15151516                        indent -= 2;
    15161517                        os << endl;
  • src/AST/SymbolTable.cpp

    r42f6e07 r2b4daf2  
    414414
    415415void SymbolTable::addFunction( const FunctionDecl * func ) {
    416         addTypes( func->type->forall );
     416        for (auto & td : func->type_params) {
     417                addType(td);
     418        }
     419        for (auto & asst : func->assertions) {
     420                addId(asst);
     421        }
     422        // addTypes( func->type->forall );
    417423        addIds( func->returns );
    418424        addIds( func->params );
  • src/AST/Type.cpp

    r42f6e07 r2b4daf2  
    2121
    2222#include "Decl.hpp"
    23 #include "ForallSubstitutor.hpp" // for substituteForall
    2423#include "Init.hpp"
    2524#include "Common/utility.h"      // for copy, move
     
    9291// GENERATED END
    9392
    94 // --- ParameterizedType
    95 
    96 void FunctionType::initWithSub(
    97         const FunctionType & o, Pass< ForallSubstitutor > & sub
    98 ) {
    99         forall = sub.core( o.forall );
    100 }
    101 
    10293// --- 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 
    11494namespace {
    11595        bool containsTtype( const std::vector<ptr<Type>> & l ) {
     
    199179                // TODO: once TypeInstType representation is updated, it should properly check
    200180                // if the context id is filled. this is a temporary hack for now
    201                 return isUnboundType(typeInst->name);
    202         }
    203         return false;
    204 }
    205 
    206 bool isUnboundType(const std::string & tname) {
    207         // xxx - look for a type name produced by renameTyVars.
    208 
    209         // TODO: once TypeInstType representation is updated, it should properly check
    210         // if the context id is filled. this is a temporary hack for now
    211         if (std::count(tname.begin(), tname.end(), '_') >= 3) {
    212                 return true;
     181                return typeInst->formal_usage > 0;
    213182        }
    214183        return false;
  • src/AST/Type.hpp

    r42f6e07 r2b4daf2  
    3636
    3737template< typename T > class Pass;
    38 
    39 struct ForallSubstitutor;
    4038
    4139class Type : public Node {
     
    272270/// Type of a function `[R1, R2](*)(P1, P2, P3)`
    273271class FunctionType final : public Type {
    274         protected:
    275         /// initializes forall with substitutor
    276         void initWithSub( const FunctionType & o, Pass< ForallSubstitutor > & sub );
    277 public:
    278         using ForallList = std::vector<ptr<TypeDecl>>;
     272public:
     273        using ForallList = std::vector<ptr<TypeInstType>>;
     274        using AssertionList = std::vector<ptr<VariableExpr>>;
    279275        ForallList forall;
     276        AssertionList assertions;
    280277
    281278        std::vector<ptr<Type>> returns;
     
    292289        : Type(q), returns(), params(), isVarArgs(va) {}
    293290
    294         FunctionType( const FunctionType & o );
     291        FunctionType( const FunctionType & o ) = default;
    295292
    296293        /// true if either the parameters or return values contain a tttype
     
    397394public:
    398395        readonly<TypeDecl> base;
     396        // previously from renameTyVars; now directly use integer fields instead of synthesized strings
     397        // a nonzero value of formal_usage indicates a formal type (only used in function type)
     398        // a zero value of formal_usage indicates an actual type (referenced inside body of parametric structs and functions)
    399399        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; }
    400418
    401419        TypeInstType(
     
    409427        TypeInstType( const TypeInstType & o ) = default;
    410428
     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
    411432        /// sets `base`, updating `kind` correctly
    412433        void set_base( const TypeDecl * );
     
    418439
    419440        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        }
    420446private:
    421447        TypeInstType * clone() const override { return new TypeInstType{ *this }; }
     
    510536
    511537bool isUnboundType(const Type * type);
    512 bool isUnboundType(const std::string & tname);
    513 
     538
     539}
     540
     541namespace 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        };
    514552}
    515553
  • src/AST/TypeEnvironment.cpp

    r42f6e07 r2b4daf2  
    5252        for ( const auto & i : open ) {
    5353                if ( first ) { first = false; } else { out << ' '; }
    54                 out << i.first << "(" << i.second << ")";
     54                out << i.first.typeString() << "(" << i.second << ")";
    5555        }
    5656}
     
    6262                if(first) first = false;
    6363                else out << " ";
    64                 if( deterministic_output && isUnboundType(var) ) out << "[unbound]";
    65                 else out << var;
     64
     65                if( deterministic_output ) out << "[unbound]";
     66                else out << "_" << var.formal_usage << "_" << var.expr_id << "_";
     67
     68                out << var.base->name;
    6669        }
    6770        out << ")";
     
    7982}
    8083
    81 const EqvClass * TypeEnvironment::lookup( const std::string & var ) const {
     84const EqvClass * TypeEnvironment::lookup( const TypeInstType::TypeEnvKey & var ) const {
    8285        for ( ClassList::const_iterator i = env.begin(); i != env.end(); ++i ) {
    8386                if ( i->vars.find( var ) != i->vars.end() ) return &*i;
     
    106109
    107110void TypeEnvironment::add( const FunctionType::ForallList & tyDecls ) {
    108         for ( const TypeDecl * tyDecl : tyDecls ) {
     111        for ( auto & tyDecl : tyDecls ) {
    109112                env.emplace_back( tyDecl );
    110113        }
     
    119122void TypeEnvironment::writeToSubstitution( TypeSubstitution & sub ) const {
    120123        for ( const auto & clz : env ) {
    121                 std::string clzRep;
     124                TypeInstType::TypeEnvKey clzRep;
     125                bool first = true;
    122126                for ( const auto & var : clz.vars ) {
    123127                        if ( clz.bound ) {
    124128                                sub.add( var, clz.bound );
    125                         } else if ( clzRep.empty() ) {
     129                        } else if ( first ) {
    126130                                clzRep = var;
     131                                first = false;
    127132                        } else {
    128                                 sub.add( var, new TypeInstType{ clzRep, clz.data.kind } );
     133                                sub.add( var, new TypeInstType{ clzRep } );
    129134                        }
    130135                }
     
    141146        struct Occurs : public ast::WithVisitorRef<Occurs> {
    142147                bool result;
    143                 std::set< std::string > vars;
     148                std::unordered_set< TypeInstType::TypeEnvKey > vars;
    144149                const TypeEnvironment & tenv;
    145150
    146                 Occurs( const std::string & var, const TypeEnvironment & env )
     151                Occurs( const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env )
    147152                : result( false ), vars(), tenv( env ) {
    148153                        if ( const EqvClass * clz = tenv.lookup( var ) ) {
     
    154159
    155160                void previsit( const TypeInstType * typeInst ) {
    156                         if ( vars.count( typeInst->name ) ) {
     161                        if ( vars.count( *typeInst ) ) {
    157162                                result = true;
    158                         } else if ( const EqvClass * clz = tenv.lookup( typeInst->name ) ) {
     163                        } else if ( const EqvClass * clz = tenv.lookup( *typeInst ) ) {
    159164                                if ( clz->bound ) {
    160165                                        clz->bound->accept( *visitor );
     
    165170
    166171        /// true if `var` occurs in `ty` under `env`
    167         bool occurs( const Type * ty, const std::string & var, const TypeEnvironment & env ) {
     172        bool occurs( const Type * ty, const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env ) {
    168173                Pass<Occurs> occur{ var, env };
    169174                maybe_accept( ty, occur );
     
    280285        // remove references from bound type, so that type variables can only bind to value types
    281286        ptr<Type> target = bindTo->stripReferences();
    282         auto tyvar = open.find( typeInst->name );
     287        auto tyvar = open.find( *typeInst );
    283288        assert( tyvar != open.end() );
    284289        if ( ! tyVarCompatible( tyvar->second, target ) ) return false;
    285         if ( occurs( target, typeInst->name, *this ) ) return false;
    286 
    287         auto it = internal_lookup( typeInst->name );
     290        if ( occurs( target, *typeInst, *this ) ) return false;
     291
     292        auto it = internal_lookup( *typeInst );
    288293        if ( it != env.end() ) {
    289294                if ( it->bound ) {
     
    308313        } else {
    309314                env.emplace_back(
    310                         typeInst->name, target, widen.first && widen.second, data );
     315                        *typeInst, target, widen.first && widen.second, data );
    311316        }
    312317        return true;
     
    318323                WidenMode widen, const SymbolTable & symtab
    319324) {
    320         auto c1 = internal_lookup( var1->name );
    321         auto c2 = internal_lookup( var2->name );
     325        auto c1 = internal_lookup( *var1 );
     326        auto c2 = internal_lookup( *var2 );
    322327
    323328        // exit early if variables already bound together
     
    333338        if ( c1 != env.end() ) {
    334339                if ( c1->bound ) {
    335                         if ( occurs( c1->bound, var2->name, *this ) ) return false;
     340                        if ( occurs( c1->bound, *var2, *this ) ) return false;
    336341                        type1 = c1->bound;
    337342                }
     
    340345        if ( c2 != env.end() ) {
    341346                if ( c2->bound ) {
    342                         if ( occurs( c2->bound, var1->name, *this ) ) return false;
     347                        if ( occurs( c2->bound, *var1, *this ) ) return false;
    343348                        type2 = c2->bound;
    344349                }
     
    378383        } else if ( c1 != env.end() ) {
    379384                // var2 unbound, add to env[c1]
    380                 c1->vars.emplace( var2->name );
     385                c1->vars.emplace( *var2 );
    381386                c1->allowWidening = widen1;
    382387                c1->data.isComplete |= data.isComplete;
    383388        } else if ( c2 != env.end() ) {
    384389                // var1 unbound, add to env[c2]
    385                 c2->vars.emplace( var1->name );
     390                c2->vars.emplace( *var1 );
    386391                c2->allowWidening = widen2;
    387392                c2->data.isComplete |= data.isComplete;
    388393        } else {
    389394                // neither var bound, create new class
    390                 env.emplace_back( var1->name, var2->name, widen1 && widen2, data );
     395                env.emplace_back( *var1, *var2, widen1 && widen2, data );
    391396        }
    392397
     
    452457}
    453458
    454 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const std::string & var ) {
     459TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const TypeInstType::TypeEnvKey & var ) {
    455460        for ( ClassList::iterator i = env.begin(); i != env.end(); ++i ) {
    456461                if ( i->vars.count( var ) ) return i;
  • src/AST/TypeEnvironment.hpp

    r42f6e07 r2b4daf2  
    5555/// recorded. More investigation is needed.
    5656struct AssertCompare {
    57         bool operator()( const DeclWithType * d1, const DeclWithType * d2 ) const {
    58                 int cmp = d1->name.compare( d2->name );
    59                 return cmp < 0 || ( cmp == 0 && d1->get_type() < d2->get_type() );
     57        bool operator()( const VariableExpr * d1, const VariableExpr * d2 ) const {
     58                int cmp = d1->var->name.compare( d2->var->name );
     59                return cmp < 0 || ( cmp == 0 && d1->result < d2->result );
    6060        }
    6161};
     
    7070
    7171/// Set of assertions pending satisfaction
    72 using AssertionSet = std::map< readonly<DeclWithType>, AssertionSetValue, AssertCompare >;
     72using AssertionSet = std::map< const VariableExpr *, AssertionSetValue, AssertCompare >;
    7373
    7474/// Set of open variables
    75 using OpenVarSet = std::unordered_map< std::string, TypeDecl::Data >;
     75using OpenVarSet = std::unordered_map< TypeInstType::TypeEnvKey, TypeDecl::Data >;
    7676
    7777/// Merges one set of open vars into another
     
    8989/// they bind to.
    9090struct EqvClass {
    91         std::set< std::string > vars;
     91        std::unordered_set< TypeInstType::TypeEnvKey > vars;
    9292        ptr<Type> bound;
    9393        bool allowWidening;
     
    101101
    102102        /// Singleton class constructor from TypeDecl
    103         EqvClass( const TypeDecl * decl )
    104         : vars{ decl->name }, bound(), allowWidening( true ), data( decl ) {}
     103        EqvClass( const TypeInstType * inst )
     104        : vars{ *inst }, bound(), allowWidening( true ), data( inst->base ) {}
    105105
    106106        /// Singleton class constructor from substitution
    107         EqvClass( const std::string & v, const Type * b )
     107        EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b )
    108108        : vars{ v }, bound( b ), allowWidening( false ), data( TypeDecl::Dtype, false ) {}
    109109
    110110        /// Single-var constructor (strips qualifiers from bound type)
    111         EqvClass( const std::string & v, const Type * b, bool w, const TypeDecl::Data & d )
     111        EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b, bool w, const TypeDecl::Data & d )
    112112        : vars{ v }, bound( b ), allowWidening( w ), data( d ) {
    113113                reset_qualifiers( bound );
     
    115115
    116116        /// Double-var constructor
    117         EqvClass( const std::string & v, const std::string & u, bool w, const TypeDecl::Data & d )
     117        EqvClass( const TypeInstType::TypeEnvKey & v, const TypeInstType::TypeEnvKey & u, bool w, const TypeDecl::Data & d )
    118118        : vars{ v, u }, bound(), allowWidening( w ), data( d ) {}
    119119
     
    131131public:
    132132        /// Finds the equivalence class containing a variable; nullptr for none such
    133         const EqvClass * lookup( const std::string & var ) const;
     133        const EqvClass * lookup( const TypeInstType::TypeEnvKey & var ) const;
    134134
    135135        /// Add a new equivalence class for each type variable
     
    207207
    208208        /// Private lookup API; returns array index of string, or env.size() for not found
    209         ClassList::iterator internal_lookup( const std::string & );
     209        ClassList::iterator internal_lookup( const TypeInstType::TypeEnvKey & );
    210210};
    211211
  • src/AST/TypeSubstitution.cpp

    r42f6e07 r2b4daf2  
    3939void TypeSubstitution::initialize( const TypeSubstitution &src, TypeSubstitution &dest ) {
    4040        dest.typeEnv.clear();
    41         dest.varEnv.clear();
    4241        dest.add( src );
    4342}
     
    4746                typeEnv[ i->first ] = i->second;
    4847        } // for
    49         for ( VarEnvType::const_iterator i = other.varEnv.begin(); i != other.varEnv.end(); ++i ) {
    50                 varEnv[ i->first ] = i->second;
    51         } // for
    5248}
    5349
    54 void TypeSubstitution::add( std::string formalType, const Type *actualType ) {
    55         typeEnv[ formalType ] = actualType;
     50void TypeSubstitution::add( const TypeInstType * formalType, const Type *actualType ) {
     51        typeEnv[ *formalType ] = actualType;
    5652}
    5753
    58 void TypeSubstitution::addVar( std::string formalExpr, const Expr *actualExpr ) {
    59         varEnv[ formalExpr ] = actualExpr;
     54void TypeSubstitution::add( const TypeInstType::TypeEnvKey & key, const Type * actualType) {
     55        typeEnv[ key ] = actualType;
    6056}
    6157
    62 void TypeSubstitution::remove( std::string formalType ) {
    63         TypeEnvType::iterator i = typeEnv.find( formalType );
     58void TypeSubstitution::remove( const TypeInstType * formalType ) {
     59        TypeEnvType::iterator i = typeEnv.find( *formalType );
    6460        if ( i != typeEnv.end() ) {
    65                 typeEnv.erase( formalType );
     61                typeEnv.erase( *formalType );
    6662        } // if
    6763}
    6864
    69 const Type *TypeSubstitution::lookup( std::string formalType ) const {
    70         TypeEnvType::const_iterator i = typeEnv.find( formalType );
     65const Type *TypeSubstitution::lookup( const TypeInstType * formalType ) const {
     66        TypeEnvType::const_iterator i = typeEnv.find( *formalType );
    7167
    7268        // break on not in substitution set
     
    7571        // attempt to transitively follow TypeInstType links.
    7672        while ( const TypeInstType *actualType = i->second.as<TypeInstType>()) {
    77                 const std::string& typeName = actualType->name;
    78 
    7973                // break cycles in the transitive follow
    80                 if ( formalType == typeName ) break;
     74                if ( *formalType == *actualType ) break;
    8175
    8276                // Look for the type this maps to, returning previous mapping if none-such
    83                 i = typeEnv.find( typeName );
     77                i = typeEnv.find( *actualType );
    8478                if ( i == typeEnv.end() ) return actualType;
    8579        }
     
    9084
    9185bool TypeSubstitution::empty() const {
    92         return typeEnv.empty() && varEnv.empty();
     86        return typeEnv.empty();
    9387}
    9488
     
    9892                TypeSubstitution * newEnv;
    9993                EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){}
    100                 void previsit( TypeDecl * tyDecl ) {
     94                void previsit( FunctionType * ftype ) {
    10195                        // transfer known bindings for seen type variables
    102                         if ( const Type * t = env->lookup( tyDecl->name ) ) {
    103                                 newEnv->add( tyDecl->name, t );
     96                        for (auto & formal : ftype->forall) {
     97                                if ( const Type * t = env->lookup( formal ) ) {
     98                                        newEnv->add( formal, t );
     99                                }
    104100                        }
    105101                }
     
    130126
    131127const Type * TypeSubstitution::Substituter::postvisit( const TypeInstType *inst ) {
    132         BoundVarsType::const_iterator bound = boundVars.find( inst->name );
     128        BoundVarsType::const_iterator bound = boundVars.find( *inst );
    133129        if ( bound != boundVars.end() ) return inst;
    134130
    135         TypeEnvType::const_iterator i = sub.typeEnv.find( inst->name );
     131        TypeEnvType::const_iterator i = sub.typeEnv.find( *inst );
    136132        if ( i == sub.typeEnv.end() ) {
    137133                return inst;
     
    141137                // TODO: investigate preventing type variables from being bound to themselves in the first place.
    142138                if ( const TypeInstType * replacement = i->second.as<TypeInstType>() ) {
    143                         if ( inst->name == replacement->name ) {
     139                        if ( *inst == *replacement ) {
    144140                                return inst;
    145141                        }
     
    156152}
    157153
    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 
    168154void TypeSubstitution::Substituter::previsit( const FunctionType * ptype ) {
    169155        GuardValue( boundVars );
    170156        // bind type variables from forall-qualifiers
    171157        if ( freeOnly ) {
    172                 for ( const TypeDecl * tyvar : ptype->forall ) {
    173                                 boundVars.insert( tyvar->name );
     158                for ( auto & tyvar : ptype->forall ) {
     159                                boundVars.insert( *tyvar );
    174160                } // for
    175161        } // if
    176162}
    177163
     164/*
    178165void TypeSubstitution::Substituter::handleAggregateType( const BaseInstType * type ) {
    179166        GuardValue( boundVars );
     
    184171                        if ( ! type->params.empty() ) {
    185172                                for ( const TypeDecl * tyvar : decl->params ) {
    186                                         boundVars.insert( tyvar->name );
     173                                        boundVars.insert( *tyvar );
    187174                                } // for
    188175                        } // if
     
    198185        handleAggregateType( aggregateUseType );
    199186}
     187*/
    200188
    201189} // namespace ast
  • src/AST/TypeSubstitution.hpp

    r42f6e07 r2b4daf2  
    6969        }
    7070
    71         void add( std::string formalType, const Type *actualType );
     71        void add( const TypeInstType * formalType, const Type *actualType );
     72        void add( const TypeInstType::TypeEnvKey & key, const Type *actualType );
    7273        void add( const TypeSubstitution &other );
    73         void remove( std::string formalType );
    74         const Type *lookup( std::string formalType ) const;
     74        void remove( const TypeInstType * formalType );
     75        const Type *lookup( const TypeInstType * formalType ) const;
    7576        bool empty() const;
    76 
    77         void addVar( std::string formalExpr, const Expr *actualExpr );
    7877
    7978        template< typename FormalIterator, typename ActualIterator >
     
    101100        friend class Pass;
    102101
    103         typedef std::unordered_map< std::string, ptr<Type> > TypeEnvType;
    104         typedef std::unordered_map< std::string, ptr<Expr> > VarEnvType;
     102        typedef std::unordered_map< TypeInstType::TypeEnvKey, ptr<Type> > TypeEnvType;
    105103        TypeEnvType typeEnv;
    106         VarEnvType varEnv;
    107104
    108105  public:
     
    113110        auto   end() const -> decltype( typeEnv.  end() ) { return typeEnv.  end(); }
    114111
    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(); }
    119112};
    120113
     114// this is the only place where type parameters outside a function formal may be substituted.
    121115template< typename FormalIterator, typename ActualIterator >
    122116void TypeSubstitution::add( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) {
     
    129123                        if ( const TypeExpr *actual = actualIt->template as<TypeExpr>() ) {
    130124                                if ( formal->name != "" ) {
    131                                         typeEnv[ formal->name ] = actual->type;
     125                                        typeEnv[ formal ] = actual->type;
    132126                                } // if
    133127                        } else {
     
    135129                        } // if
    136130                } else {
    137                         // TODO: type check the formal and actual parameters
    138                         if ( (*formalIt)->name != "" ) {
    139                                 varEnv[ (*formalIt)->name ] = *actualIt;
    140                         } // if
     131                       
    141132                } // if
    142133        } // for
    143134}
     135
     136
    144137
    145138template< typename FormalIterator, typename ActualIterator >
     
    147140        add( formalBegin, formalEnd, actualBegin );
    148141}
     142
    149143
    150144} // namespace ast
     
    164158
    165159                const Type * postvisit( const TypeInstType * aggregateUseType );
    166                 const Expr * postvisit( const NameExpr * nameExpr );
    167160
    168161                /// Records type variable bindings from forall-statements
    169162                void previsit( const FunctionType * type );
    170163                /// Records type variable bindings from forall-statements and instantiations of generic types
    171                 void handleAggregateType( const BaseInstType * type );
     164                // void handleAggregateType( const BaseInstType * type );
    172165
    173                 void previsit( const StructInstType * aggregateUseType );
    174                 void previsit( const UnionInstType * aggregateUseType );
     166                // void previsit( const StructInstType * aggregateUseType );
     167                // void previsit( const UnionInstType * aggregateUseType );
    175168
    176169                const TypeSubstitution & sub;
    177170                int subCount = 0;
    178171                bool freeOnly;
    179                 typedef std::unordered_set< std::string > BoundVarsType;
     172                typedef std::unordered_set< TypeInstType::TypeEnvKey > BoundVarsType;
    180173                BoundVarsType boundVars;
    181174
  • src/AST/module.mk

    r42f6e07 r2b4daf2  
    3333        AST/Expr.cpp \
    3434        AST/Expr.hpp \
    35         AST/ForallSubstitutionTable.cpp \
    36         AST/ForallSubstitutionTable.hpp \
    37         AST/ForallSubstitutor.hpp \
    3835        AST/FunctionSpec.hpp \
    3936        AST/Fwd.hpp \
  • src/GenPoly/GenPoly.cc

    r42f6e07 r2b4daf2  
    115115                if (!env) return type;
    116116                if (auto typeInst = dynamic_cast<const ast::TypeInstType*> (type)) {
    117                         auto newType = env->lookup(typeInst->name);
     117                        auto newType = env->lookup(typeInst);
    118118                        if (newType) return newType;
    119119                }
     
    172172
    173173                if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( type ) ) {
    174                         return tyVars.find(typeInst->name) != tyVars.end() ? type : nullptr;
     174                        return tyVars.find(typeInst->typeString()) != tyVars.end() ? type : nullptr;
    175175                } else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
    176176                        return isPolyType( arrayType->base, env );
     
    552552        }
    553553
    554         void addToTyVarMap( const ast::TypeDecl * tyVar, TyVarMap & tyVarMap) {
    555                 tyVarMap.insert(tyVar->name, convData(ast::TypeDecl::Data{tyVar}));
     554        void addToTyVarMap( const ast::TypeInstType * tyVar, TyVarMap & tyVarMap) {
     555                tyVarMap.insert(tyVar->typeString(), convData(ast::TypeDecl::Data{tyVar->base}));
    556556        }
    557557
  • src/InitTweak/GenInit.cc

    r42f6e07 r2b4daf2  
    122122        };
    123123
     124        struct HoistArrayDimension_NoResolve final : public WithDeclsToAdd, public WithShortCircuiting, public WithGuards {
     125                /// hoist dimension from array types in object declaration so that it uses a single
     126                /// const variable of type size_t, so that side effecting array dimensions are only
     127                /// computed once.
     128                static void hoistArrayDimension( std::list< Declaration * > & translationUnit );
     129
     130                void premutate( ObjectDecl * objectDecl );
     131                DeclarationWithType * postmutate( ObjectDecl * objectDecl );
     132                void premutate( FunctionDecl *functionDecl );
     133                // should not traverse into any of these declarations to find objects
     134                // that need to be constructed or destructed
     135                void premutate( AggregateDecl * ) { visit_children = false; }
     136                void premutate( NamedTypeDecl * ) { visit_children = false; }
     137                void premutate( FunctionType * ) { visit_children = false; }
     138
     139                void hoist( Type * type );
     140
     141                Type::StorageClasses storageClasses;
     142                bool inFunction = false;
     143        };
     144
    124145        void genInit( std::list< Declaration * > & translationUnit ) {
    125                 HoistArrayDimension::hoistArrayDimension( translationUnit );
     146                if (!useNewAST) {
     147                        HoistArrayDimension::hoistArrayDimension( translationUnit );
     148                }
     149                else {
     150                        HoistArrayDimension_NoResolve::hoistArrayDimension( translationUnit );
     151                }
    126152                fixReturnStatements( translationUnit );
    127153
     
    196222
    197223                        // need to resolve array dimensions in order to accurately determine if constexpr
    198                         if (!useNewAST) {
    199                                 ResolvExpr::findSingleExpression( arrayType->dimension, Validate::SizeType->clone(), indexer );
    200                                 // array is variable-length when the dimension is not constexpr
    201                                 arrayType->isVarLen = ! isConstExpr( arrayType->dimension );
    202                         }
     224                        ResolvExpr::findSingleExpression( arrayType->dimension, Validate::SizeType->clone(), indexer );
     225                        // array is variable-length when the dimension is not constexpr
     226                        arrayType->isVarLen = ! isConstExpr( arrayType->dimension );
    203227                        // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects.
    204228                        // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve
     
    218242
    219243        void HoistArrayDimension::premutate( FunctionDecl * ) {
     244                GuardValue( inFunction );
     245                inFunction = true;
     246        }
     247
     248        // precompute array dimension expression, because constructor generation may duplicate it,
     249        // which would be incorrect if it is a side-effecting computation.
     250        void HoistArrayDimension_NoResolve::hoistArrayDimension( std::list< Declaration * > & translationUnit ) {
     251                PassVisitor<HoistArrayDimension_NoResolve> hoister;
     252                mutateAll( translationUnit, hoister );
     253        }
     254
     255        void HoistArrayDimension_NoResolve::premutate( ObjectDecl * objectDecl ) {
     256                GuardValue( storageClasses );
     257                storageClasses = objectDecl->get_storageClasses();
     258        }
     259
     260        DeclarationWithType * HoistArrayDimension_NoResolve::postmutate( ObjectDecl * objectDecl ) {
     261                hoist( objectDecl->get_type() );
     262                return objectDecl;
     263        }
     264
     265        void HoistArrayDimension_NoResolve::hoist( Type * type ) {
     266                // if in function, generate const size_t var
     267                static UniqueName dimensionName( "_array_dim" );
     268
     269                // C doesn't allow variable sized arrays at global scope or for static variables, so don't hoist dimension.
     270                if ( ! inFunction ) return;
     271                if ( storageClasses.is_static ) return;
     272
     273                if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) {
     274                        if ( ! arrayType->get_dimension() ) return; // xxx - recursive call to hoist?
     275                        // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects.
     276                        // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve
     277                        // still try to detect constant expressions
     278                        if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return;
     279
     280                        ObjectDecl * arrayDimension = new ObjectDecl( dimensionName.newName(), storageClasses, LinkageSpec::C, 0, Validate::SizeType->clone(), new SingleInit( arrayType->get_dimension() ) );
     281                        arrayDimension->get_type()->set_const( true );
     282
     283                        arrayType->set_dimension( new VariableExpr( arrayDimension ) );
     284                        declsToAddBefore.push_back( arrayDimension );
     285
     286                        hoist( arrayType->get_base() );
     287                        return;
     288                }
     289        }
     290
     291        void HoistArrayDimension_NoResolve::premutate( FunctionDecl * ) {
    220292                GuardValue( inFunction );
    221293                inFunction = true;
  • src/ResolvExpr/AdjustExprType.cc

    r42f6e07 r2b4daf2  
    133133                const ast::Type * postvisit( const ast::TypeInstType * inst ) {
    134134                        // replace known function-type-variables with pointer-to-function
    135                         if ( const ast::EqvClass * eqvClass = tenv.lookup( inst->name ) ) {
     135                        if ( const ast::EqvClass * eqvClass = tenv.lookup( *inst ) ) {
    136136                                if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
    137137                                        return new ast::PointerType{ inst };
  • src/ResolvExpr/CandidateFinder.cpp

    r42f6e07 r2b4daf2  
    212212                // mark type variable and specialization cost of forall clause
    213213                convCost.incVar( function->forall.size() );
    214                 for ( const ast::TypeDecl * td : function->forall ) {
    215                         convCost.decSpec( td->assertions.size() );
    216                 }
     214                convCost.decSpec( function->assertions.size() );
    217215
    218216                return convCost;
     
    223221                ast::AssertionSet & need
    224222        ) {
    225                 for ( const ast::TypeDecl * tyvar : type->forall ) {
    226                         unifiableVars[ tyvar->name ] = ast::TypeDecl::Data{ tyvar };
    227                         for ( const ast::DeclWithType * assn : tyvar->assertions ) {
    228                                 need[ assn ].isUsed = true;
    229                         }
     223                for ( auto & tyvar : type->forall ) {
     224                        unifiableVars[ *tyvar ] = ast::TypeDecl::Data{ tyvar->base };
     225                }
     226                for ( auto & assn : type->assertions ) {
     227                        need[ assn ].isUsed = true;
    230228                }
    231229        }
     
    907905                        // xxx - is it possible that handleTupleAssignment and main finder both produce candidates?
    908906                        // this means there exists ctor/assign functions with a tuple as first parameter.
    909                         funcFinder.find( untypedExpr->func, selfFinder.candidates.empty() ? ResolvMode::withAdjustment() : ResolvMode::withoutFailFast() );
     907                        ResolvMode mode = {
     908                                true, // adjust
     909                                !untypedExpr->func.as<ast::NameExpr>(), // prune if not calling by name
     910                                selfFinder.candidates.empty() // failfast if other options are not found
     911                        };
     912                        funcFinder.find( untypedExpr->func, mode );
    910913                        // short-circuit if no candidates
    911914                        // if ( funcFinder.candidates.empty() ) return;
     
    953956                                                auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult )
    954957                                        ) {
    955                                                 if ( const ast::EqvClass * clz = func->env.lookup( inst->name ) ) {
     958                                                if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) {
    956959                                                        if ( auto function = clz->bound.as< ast::FunctionType >() ) {
    957960                                                                CandidateRef newFunc{ new Candidate{ *func } };
     
    10771080                        assert( toType );
    10781081                        toType = resolveTypeof( toType, symtab );
    1079                         toType = SymTab::validateType( castExpr->location, toType, symtab );
     1082                        // toType = SymTab::validateType( castExpr->location, toType, symtab );
    10801083                        toType = adjustExprType( toType, tenv, symtab );
    10811084
     
    11621165
    11631166                                        if(auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) {
    1164                                                 auto td = cand->env.lookup(insttype->name);
     1167                                                auto td = cand->env.lookup(*insttype);
    11651168                                                if(!td) { continue; }
    11661169                                                expr = td->bound.get();
     
    15681571                                // calculate target type
    15691572                                const ast::Type * toType = resolveTypeof( initAlt.type, symtab );
    1570                                 toType = SymTab::validateType( initExpr->location, toType, symtab );
     1573                                // toType = SymTab::validateType( initExpr->location, toType, symtab );
    15711574                                toType = adjustExprType( toType, tenv, symtab );
    15721575                                // The call to find must occur inside this loop, otherwise polymorphic return
  • src/ResolvExpr/CastCost.cc

    r42f6e07 r2b4daf2  
    202202) {
    203203        if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    204                 if ( const ast::EqvClass * eqvClass = env.lookup( typeInst->name ) ) {
     204                if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst ) ) {
    205205                        // check cast cost against bound type, if present
    206206                        if ( eqvClass->bound ) {
  • src/ResolvExpr/CommonType.cc

    r42f6e07 r2b4daf2  
    713713                        const ast::Type * base = oPtr->base;
    714714                        if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) {
    715                                 auto entry = open.find( var->name );
     715                                auto entry = open.find( *var );
    716716                                if ( entry != open.end() ) {
    717717                                        ast::AssertionSet need, have;
  • src/ResolvExpr/ConversionCost.cc

    r42f6e07 r2b4daf2  
    498498) {
    499499        if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    500                 if ( const ast::EqvClass * eqv = env.lookup( inst->name ) ) {
     500                if ( const ast::EqvClass * eqv = env.lookup( *inst ) ) {
    501501                        if ( eqv->bound ) {
    502502                                return conversionCost(src, eqv->bound, srcIsLvalue, symtab, env );
     
    675675
    676676void ConversionCost_new::postvisit( const ast::TypeInstType * typeInstType ) {
    677         if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) {
     677        if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) {
    678678                cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env );
    679679        } else if ( const ast::TypeInstType * dstAsInst =
    680680                        dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    681                 if ( typeInstType->name == dstAsInst->name ) {
     681                if ( *typeInstType == *dstAsInst ) {
    682682                        cost = Cost::zero;
    683683                }
  • src/ResolvExpr/FindOpenVars.cc

    r42f6e07 r2b4daf2  
    112112                                // mark open/closed variables
    113113                                if ( nextIsOpen ) {
    114                                         for ( const ast::TypeDecl * decl : type->forall ) {
    115                                                 open[ decl->name ] = ast::TypeDecl::Data{ decl };
    116                                                 for ( const ast::DeclWithType * assert : decl->assertions ) {
    117                                                         need[ assert ].isUsed = false;
    118                                                 }
     114                                        for ( auto & decl : type->forall ) {
     115                                                open[ *decl ] = ast::TypeDecl::Data{ decl->base };
     116                                        }
     117                                        for ( auto & assert : type->assertions ) {
     118                                                need[ assert ].isUsed = false;
    119119                                        }
    120120                                } else {
    121                                         for ( const ast::TypeDecl * decl : type->forall ) {
    122                                                 closed[ decl->name ] = ast::TypeDecl::Data{ decl };
    123                                                 for ( const ast::DeclWithType * assert : decl->assertions ) {
    124                                                         have[ assert ].isUsed = false;
    125                                                 }
     121                                        for ( auto & decl : type->forall ) {
     122                                                closed[ *decl ] = ast::TypeDecl::Data{ decl->base };   
     123                                        }
     124                                        for ( auto & assert : type->assertions ) {
     125                                                have[ assert ].isUsed = false;
    126126                                        }
    127127                                }
  • src/ResolvExpr/PolyCost.cc

    r42f6e07 r2b4daf2  
    6868
    6969        void previsit( const ast::TypeInstType * type ) {
    70                 if ( const ast::EqvClass * eqv = env_.lookup( type->name ) ) /* && */ if ( eqv->bound ) {
     70                if ( const ast::EqvClass * eqv = env_.lookup( *type ) ) /* && */ if ( eqv->bound ) {
    7171                        if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) {
    7272                                if ( symtab.lookupType( otherType->name ) ) {
  • src/ResolvExpr/PtrsAssignable.cc

    r42f6e07 r2b4daf2  
    134134        }
    135135        void postvisit( const ast::TypeInstType * inst ) {
    136                 if ( const ast::EqvClass * eqv = typeEnv.lookup( inst->name ) ) {
     136                if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst ) ) {
    137137                        if ( eqv->bound ) {
    138138                                // T * = S * for any S depends on the type bound to T
     
    146146                const ast::TypeEnvironment & env ) {
    147147        if ( const ast::TypeInstType * dstAsInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    148                 if ( const ast::EqvClass * eqv = env.lookup( dstAsInst->name ) ) {
     148                if ( const ast::EqvClass * eqv = env.lookup( *dstAsInst ) ) {
    149149                        return ptrsAssignable( src, eqv->bound, env );
    150150                }
  • src/ResolvExpr/PtrsCastable.cc

    r42f6e07 r2b4daf2  
    180180                                        }
    181181                                }
    182                         } else if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) {
     182                        } else if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
    183183                                if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
    184184                                        return -1;
     
    283283) {
    284284        if ( auto inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    285                 if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) {
     285                if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
    286286                        return ptrsAssignable( src, eqvClass->bound, env );
    287287                }
  • src/ResolvExpr/RenameVars.cc

    r42f6e07 r2b4daf2  
    1919#include <utility>                 // for pair
    2020
    21 #include "AST/ForallSubstitutionTable.hpp"
    2221#include "AST/Pass.hpp"
    2322#include "AST/Type.hpp"
     
    3938                int level = 0;
    4039                int resetCount = 0;
     40
     41                int next_expr_id = 1;
     42                int next_usage_id = 1;
    4143                ScopedMap< std::string, std::string > nameMap;
     44                ScopedMap< std::string, ast::TypeInstType::TypeEnvKey > idMap;
    4245        public:
    43                 ast::ForallSubstitutionTable subs;
    44 
    4546                void reset() {
    4647                        level = 0;
     
    5354                                type->name = it->second;
    5455                        }
     56                }
     57
     58                void nextUsage() {
     59                        ++next_usage_id;
    5560                }
    5661
     
    7883
    7984                const ast::TypeInstType * rename( const ast::TypeInstType * type ) {
    80                         // re-linking of base type handled by WithForallSubstitutor
    81 
    8285                        // rename
    83                         auto it = nameMap.find( type->name );
    84                         if ( it != nameMap.end() ) {
    85                                 // unconditionally mutate because map will *always* have different name,
    86                                 // if this mutates, will *always* have been mutated by ForallSubstitutor above
    87                                 ast::TypeInstType * mut = ast::mutate( type );
    88                                 mut->name = it->second;
     86                        auto it = idMap.find( type->name );
     87                        if ( it != idMap.end() ) {
     88                                // unconditionally mutate because map will *always* have different name
     89                                ast::TypeInstType * mut = ast::shallowCopy( type );
     90                                // reconcile base node since some copies might have been made
     91                                mut->base = it->second.base;
     92                                mut->formal_usage = it->second.formal_usage;
     93                                mut->expr_id = it->second.expr_id;
    8994                    type = mut;
    9095                        }
     
    9398                }
    9499
    95                 const ast::FunctionType * openLevel( const ast::FunctionType * type ) {
     100                const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) {
    96101                        if ( type->forall.empty() ) return type;
    97 
    98                         nameMap.beginScope();
     102                        idMap.beginScope();
    99103
    100104                        // Load new names from this forall clause and perform renaming.
    101                         auto mutType = ast::mutate( type );
    102                         assert( type == mutType && "mutated type must be unique from ForallSubstitutor" );
    103                         for ( ast::ptr< ast::TypeDecl > & td : mutType->forall ) {
    104                                 assertf(dynamic_cast<ast::FunctionType *>(mutType), "renaming vars in non-function type");
    105                                 std::ostringstream output;
    106                                 output << "_" << resetCount << "_" << level << "_" << td->name;
    107                                 std::string newname =  output.str();
    108                                 nameMap[ td->name ] = newname;
    109                                 ++level;
    110 
    111                                 ast::TypeDecl * mutDecl = ast::mutate( td.get() );
    112                                 assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" );
    113                                 mutDecl->name = newname;
    114                                 // assertion above means `td = mutDecl;` is unnecessary
    115                         }
    116                         // assertion above means `type = mutType;` is unnecessary
    117 
    118                         return type;
     105                        auto mutType = ast::shallowCopy( type );
     106                        // assert( type == mutType && "mutated type must be unique from ForallSubstitutor" );
     107                        for ( auto & td : mutType->forall ) {
     108                                auto mut = ast::shallowCopy( td.get() );
     109                                // assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" );
     110
     111                                if (mode == GEN_EXPR_ID) {
     112                                        mut->expr_id = next_expr_id;
     113                                        mut->formal_usage = -1;
     114                                        ++next_expr_id;
     115                                }
     116                                else if (mode == GEN_USAGE) {
     117                                        assertf(mut->expr_id, "unfilled expression id in generating candidate type");
     118                                        mut->formal_usage = next_usage_id;
     119                                }
     120                                else {
     121                                        assert(false);
     122                                }
     123                                idMap[ td->name ] = ast::TypeInstType::TypeEnvKey(*mut);
     124                               
     125                                td = mut;
     126                        }
     127
     128                        return mutType;
    119129                }
    120130
    121131                void closeLevel( const ast::FunctionType * type ) {
    122132                        if ( type->forall.empty() ) return;
    123 
    124                         nameMap.endScope();
     133                        idMap.endScope();
    125134                }
    126135        };
     
    142151        };
    143152
    144         struct RenameVars_new /*: public ast::WithForallSubstitutor*/ {
    145                 #warning when old RenameVars goes away, replace hack below with global pass inheriting from WithForallSubstitutor
    146                 ast::ForallSubstitutionTable & subs = renaming.subs;
     153        struct RenameVars_new : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ {
     154                RenameMode mode;
    147155
    148156                const ast::FunctionType * previsit( const ast::FunctionType * type ) {
    149                         return renaming.openLevel( type );
     157                        return renaming.openLevel( type, mode );
    150158                }
    151159
     
    163171
    164172                const ast::TypeInstType * previsit( const ast::TypeInstType * type ) {
     173                        if (mode == GEN_USAGE && !type->formal_usage) return type; // do not rename an actual type
    165174                        return renaming.rename( type );
    166175                }
     
    177186}
    178187
    179 const ast::Type * renameTyVars( const ast::Type * t ) {
    180         ast::Type *tc = ast::deepCopy(t);
     188const ast::Type * renameTyVars( const ast::Type * t, RenameMode mode, bool reset ) {
     189        // ast::Type *tc = ast::deepCopy(t);
    181190        ast::Pass<RenameVars_new> renamer;
    182 //      return t->accept( renamer );
    183         return tc->accept( renamer );
     191        renamer.core.mode = mode;
     192        if (mode == GEN_USAGE && reset) {
     193                renaming.nextUsage();
     194        }
     195        return t->accept( renamer );
    184196}
    185197
    186198void resetTyVarRenaming() {
    187199        renaming.reset();
     200        renaming.nextUsage();
    188201}
    189202
  • src/ResolvExpr/RenameVars.h

    r42f6e07 r2b4daf2  
    3030        /// Provides a consistent renaming of forall type names in a hierarchy by prefixing them with a unique "level" ID
    3131        void renameTyVars( Type * );
    32         const ast::Type * renameTyVars( const ast::Type * );
     32
     33        enum RenameMode {
     34                GEN_USAGE, // for type in VariableExpr
     35                GEN_EXPR_ID // for type in decl
     36        };
     37        const ast::Type * renameTyVars( const ast::Type *, RenameMode mode = GEN_USAGE, bool reset = true );
     38       
    3339
    3440        /// resets internal state of renamer to avoid overflow
    3541        void resetTyVarRenaming();
     42
     43       
    3644} // namespace ResolvExpr
    3745
  • src/ResolvExpr/ResolvMode.h

    r42f6e07 r2b4daf2  
    2323                const bool failFast;         ///< Fail on no resulting alternatives? [true]
    2424
    25         private:
    2625                constexpr ResolvMode(bool a, bool p, bool ff)
    2726                : adjust(a), prune(p), failFast(ff) {}
    2827
    29         public:
    3028                /// Default settings
    3129                constexpr ResolvMode() : adjust(false), prune(true), failFast(true) {}
  • src/ResolvExpr/ResolveAssertions.cc

    r42f6e07 r2b4daf2  
    397397
    398398        /// Limit to depth of recursion of assertion satisfaction
    399         static const int recursionLimit = 4;
     399        static const int recursionLimit = 7;
    400400        /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of
    401401        static const int deferLimit = 10;
  • src/ResolvExpr/ResolveTypeof.cc

    r42f6e07 r2b4daf2  
    1515
    1616#include "ResolveTypeof.h"
     17#include "RenameVars.h"
    1718
    1819#include <cassert>               // for assert
     
    218219                        mutDecl->mangleName = Mangle::mangle(mutDecl); // do not mangle unnamed variables
    219220               
     221                mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
    220222                mutDecl->isTypeFixed = true;
    221223                return mutDecl;
  • src/ResolvExpr/Resolver.cc

    r42f6e07 r2b4daf2  
    986986                };
    987987        } // anonymous namespace
    988 
    989988        /// Check if this expression is or includes a deleted expression
    990989        const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) {
     
    11521151                        const ast::Expr * untyped, const ast::SymbolTable & symtab
    11531152                ) {
    1154                         resetTyVarRenaming();
    11551153                        ast::TypeEnvironment env;
    11561154                        ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, symtab, env );
     
    13121310        }
    13131311
    1314         ast::ptr< ast::Expr > resolveStmtExpr(
     1312        const ast::Expr * resolveStmtExpr(
    13151313                const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab
    13161314        ) {
    13171315                assert( stmtExpr );
    13181316                ast::Pass< Resolver_new > resolver{ symtab };
    1319                 ast::ptr< ast::Expr > ret = stmtExpr;
    1320                 ret = ret->accept( resolver );
    1321                 strict_dynamic_cast< ast::StmtExpr * >( ret.get_and_mutate() )->computeResult();
     1317                auto ret = mutate(stmtExpr->accept(resolver));
     1318                strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult();
    13221319                return ret;
    13231320        }
     
    13751372                        }
    13761373
    1377                         // handle assertions. (seems deep)
     1374                        // handle assertions
    13781375
    13791376                        symtab.enterScope();
    1380                         for (auto & typeParam : mutType->forall) {
    1381                                 auto mutParam = typeParam.get_and_mutate();
    1382                                 symtab.addType(mutParam);
    1383                                 for (auto & asst : mutParam->assertions) {
    1384                                         asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab);
    1385                                         symtab.addId(asst);
    1386                                 }
    1387                                 typeParam = mutParam;
     1377                        mutType->forall.clear();
     1378                        mutType->assertions.clear();
     1379                        for (auto & typeParam : mutDecl->type_params) {
     1380                                symtab.addType(typeParam);
     1381                                mutType->forall.emplace_back(new ast::TypeInstType(typeParam->name, typeParam));
     1382                        }
     1383                        for (auto & asst : mutDecl->assertions) {
     1384                                asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab);
     1385                                symtab.addId(asst);
     1386                                mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));
    13881387                        }
    13891388
     
    14071406                        mutType->returns = std::move(returnTypes);
    14081407
     1408                        auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));
     1409
    14091410                        std::list<ast::ptr<ast::Stmt>> newStmts;
    14101411                        resolveWithExprs (mutDecl->withExprs, newStmts);
     
    14181419                        symtab.leaveScope();
    14191420
     1421                        mutDecl->type = renamedType;
    14201422                        mutDecl->mangleName = Mangle::mangle(mutDecl);
    14211423                        mutDecl->isTypeFixed = true;
     
    15341536        const PtrType * handlePtrType( const PtrType * type, const ast::SymbolTable & symtab ) {
    15351537                if ( type->dimension ) {
    1536                         #warning should use new equivalent to Validate::SizeType rather than sizeType here
    1537                         ast::ptr< ast::Type > sizeType = new ast::BasicType{ ast::BasicType::LongUnsignedInt };
     1538                        ast::ptr< ast::Type > sizeType = ast::sizeType;
    15381539                        ast::mutate_field(
    15391540                                type, &PtrType::dimension,
  • src/ResolvExpr/Resolver.h

    r42f6e07 r2b4daf2  
    7272        ast::ptr< ast::Init > resolveCtorInit(
    7373                const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab );
    74         /// Resolves a statement expression
    75         ast::ptr< ast::Expr > resolveStmtExpr(
     74        /// Resolves a statement expression 
     75        const ast::Expr * resolveStmtExpr(
    7676                const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab );
    7777} // namespace ResolvExpr
  • src/ResolvExpr/SatisfyAssertions.cpp

    r42f6e07 r2b4daf2  
    5757                ast::UniqueId resnSlot;          ///< Slot for any recursive assertion IDs
    5858
    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,
    6161                        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 ) ),
    6363                  need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {}
    6464        };
     
    6969        /// Reference to a single deferred item
    7070        struct DeferRef {
    71                 const ast::DeclWithType * decl;
     71                const ast::VariableExpr * expr;
    7272                const ast::AssertionSetValue & info;
    7373                const AssnCandidate & match;
    7474        };
    75        
    76         /// Wrapper for the deferred items from a single assertion satisfaction. 
     75
     76        /// Wrapper for the deferred items from a single assertion satisfaction.
    7777        /// Acts like an indexed list of DeferRef
    7878        struct DeferItem {
    79                 const ast::DeclWithType * decl;
     79                const ast::VariableExpr * expr;
    8080                const ast::AssertionSetValue & info;
    8181                AssnCandidateList matches;
    8282
    83                 DeferItem( 
    84                         const ast::DeclWithType * d, const ast::AssertionSetValue & i, AssnCandidateList && ms )
    85                 : decl( d ), info( i ), matches( std::move( ms ) ) {}
     83                DeferItem(
     84                        const ast::VariableExpr * d, const ast::AssertionSetValue & i, AssnCandidateList && ms )
     85                : expr( d ), info( i ), matches( std::move( ms ) ) {}
    8686
    8787                bool empty() const { return matches.empty(); }
     
    8989                AssnCandidateList::size_type size() const { return matches.size(); }
    9090
    91                 DeferRef operator[] ( unsigned i ) const { return { decl, info, matches[i] }; }
     91                DeferRef operator[] ( unsigned i ) const { return { expr, info, matches[i] }; }
    9292        };
    9393
     
    117117                /// Initial satisfaction state for a candidate
    118118                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 },
    120120                  symtab( syms ) { need.swap( c->need ); }
    121                
     121
    122122                /// Update satisfaction state for next step after previous state
    123123                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 ) ),
    126126                  symtab( o.symtab ) { costs.emplace_back( Cost::zero ); }
    127                
     127
    128128                /// Field-wise next step constructor
    129129                SatState(
    130                         CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs, 
     130                        CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs,
    131131                        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(),
    133133                  inferred( std::move( i ) ), costs( std::move( cs ) ), symtab( std::move( syms ) )
    134134                  { costs.emplace_back( Cost::zero ); }
     
    138138        void addToSymbolTable( const ast::AssertionSet & have, ast::SymbolTable & symtab ) {
    139139                for ( auto & i : have ) {
    140                         if ( i.second.isUsed ) { symtab.addId( i.first ); }
     140                        if ( i.second.isUsed ) { symtab.addId( i.first->var ); }
    141141                }
    142142        }
    143143
    144144        /// Binds a single assertion, updating satisfaction state
    145         void bindAssertion( 
    146                 const ast::DeclWithType * decl, const ast::AssertionSetValue & info, CandidateRef & cand,
     145        void bindAssertion(
     146                const ast::VariableExpr * expr, const ast::AssertionSetValue & info, CandidateRef & cand,
    147147                AssnCandidate & match, InferCache & inferred
    148148        ) {
    149149                const ast::DeclWithType * candidate = match.cdata.id;
    150                 assertf( candidate->uniqueId, 
     150                assertf( candidate->uniqueId,
    151151                        "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() );
    152                
     152
    153153                ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cvtCost );
    154154                varExpr->result = match.adjType;
     
    156156
    157157                // place newly-inferred assertion in proper location in cache
    158                 inferred[ info.resnSlot ][ decl->uniqueId ] = ast::ParamEntry{
    159                         candidate->uniqueId, candidate, match.adjType, decl->get_type(), varExpr };
     158                inferred[ info.resnSlot ][ expr->var->uniqueId ] = ast::ParamEntry{
     159                        candidate->uniqueId, candidate, match.adjType, expr->result, varExpr };
    160160        }
    161161
     
    169169
    170170                std::vector<ast::SymbolTable::IdData> candidates;
    171                 auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->name);
     171                auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->var->name);
    172172                if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {
    173173                        // prefilter special decls by argument type, if already known
    174                         ast::ptr<ast::Type> thisArgType = strict_dynamic_cast<const ast::PointerType *>(assn.first->get_type())->base
     174                        ast::ptr<ast::Type> thisArgType = assn.first->result.strict_as<ast::PointerType>()->base
    175175                                .strict_as<ast::FunctionType>()->params[0]
    176176                                .strict_as<ast::ReferenceType>()->base;
     
    184184                }
    185185                else {
    186                         candidates = sat.symtab.lookupId(assn.first->name);
     186                        candidates = sat.symtab.lookupId(assn.first->var->name);
    187187                }
    188188                for ( const ast::SymbolTable::IdData & cdata : candidates ) {
     
    200200                        ast::TypeEnvironment newEnv{ sat.cand->env };
    201201                        ast::OpenVarSet newOpen{ sat.cand->open };
    202                         ast::ptr< ast::Type > toType = assn.first->get_type();
    203                         ast::ptr< ast::Type > adjType = 
    204                                 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) );
     202                        ast::ptr< ast::Type > toType = assn.first->result;
     203                        ast::ptr< ast::Type > adjType =
     204                                renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ), GEN_USAGE, false );
    205205
    206206                        // only keep candidates which unify
     
    213213                                }
    214214
    215                                 matches.emplace_back( 
     215                                matches.emplace_back(
    216216                                        cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
    217217                                        std::move( newOpen ), crntResnSlot );
     
    287287        };
    288288
    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
    290290        /// 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
    294294        ) {
    295295                // prune if cheaper alternative for same key has already been generated
     
    308308        }
    309309
    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`
    312312        /// for description of "combo iterator".
    313313        class CandidateEnvMerger {
     
    337337                                        // compute conversion cost from satisfying decl to assertion
    338338                                        cost += computeConversionCost(
    339                                                 assn.match.adjType, assn.decl->get_type(), false, symtab, env );
     339                                                assn.match.adjType, assn.expr->result, false, symtab, env );
    340340
    341341                                        // mark vars+specialization on function-type assertions
     
    350350                                        cost.incVar( func->forall.size() );
    351351
    352                                         for ( const ast::TypeDecl * td : func->forall ) {
    353                                                 cost.decSpec( td->assertions.size() );
    354                                         }
     352                                        cost.decSpec( func->assertions.size() );
    355353                                }
    356354                        }
     
    387385
    388386        /// Limit to depth of recursion of assertion satisfaction
    389         static const int recursionLimit = 4;
     387        static const int recursionLimit = 8;
    390388        /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of
    391389        static const int deferLimit = 10;
    392390} // anonymous namespace
    393391
    394 void satisfyAssertions( 
    395         CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 
     392void satisfyAssertions(
     393        CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out,
    396394        std::vector<std::string> & errors
    397395) {
     
    419417                        if ( it != thresholds.end() && it->second < sat.costs ) goto nextSat;
    420418
    421                         // make initial pass at matching assertions
    422                         for ( auto & assn : sat.need ) {
    423                                 // fail early if any assertion is not satisfiable
    424                                 if ( ! satisfyAssertion( assn, sat ) ) {
     419                        // should a limit be imposed? worst case here is O(n^2) but very unlikely to happen.
     420                        for (unsigned resetCount = 0; ; ++resetCount) {
     421                                ast::AssertionList next;
     422                                resetTyVarRenaming();
     423                                // make initial pass at matching assertions
     424                                for ( auto & assn : sat.need ) {
     425                                        // fail early if any assertion is not satisfiable
     426                                        if ( ! satisfyAssertion( assn, sat ) ) {
     427                                                next.emplace_back(assn);
     428                                                // goto nextSat;
     429                                        }
     430                                }
     431                                // success
     432                                if (next.empty()) break;
     433                                // fail if nothing resolves
     434                                else if (next.size() == sat.need.size()) {
    425435                                        Indenter tabs{ 3 };
    426436                                        std::ostringstream ss;
     
    428438                                        print( ss, *sat.cand, ++tabs );
    429439                                        ss << (tabs-1) << "Could not satisfy assertion:\n";
    430                                         ast::print( ss, assn.first, tabs );
     440                                        ast::print( ss, next[0].first, tabs );
    431441
    432442                                        errors.emplace_back( ss.str() );
    433443                                        goto nextSat;
    434444                                }
     445                                sat.need = std::move(next);
    435446                        }
    436447
     
    438449                                // either add successful match or push back next state
    439450                                if ( sat.newNeed.empty() ) {
    440                                         finalizeAssertions( 
     451                                        finalizeAssertions(
    441452                                                sat.cand, sat.inferred, thresholds, std::move( sat.costs ), out );
    442453                                } else {
     
    451462                                ss << (tabs-1) << "Too many non-unique satisfying assignments for assertions:\n";
    452463                                for ( const auto & d : sat.deferred ) {
    453                                         ast::print( ss, d.decl, tabs );
     464                                        ast::print( ss, d.expr, tabs );
    454465                                }
    455466
     
    460471                                std::vector< CandidateEnvMerger::OutType > compatible = filterCombos(
    461472                                        sat.deferred, CandidateEnvMerger{ sat.cand->env, sat.cand->open, sat.symtab } );
    462                                
     473
    463474                                // fail early if no mutually-compatible assertion satisfaction
    464475                                if ( compatible.empty() ) {
     
    469480                                        ss << (tabs-1) << "No mutually-compatible satisfaction for assertions:\n";
    470481                                        for ( const auto& d : sat.deferred ) {
    471                                                 ast::print( ss, d.decl, tabs );
     482                                                ast::print( ss, d.expr, tabs );
    472483                                        }
    473484
     
    483494                                        // set up next satisfaction state
    484495                                        CandidateRef nextCand = std::make_shared<Candidate>(
    485                                                 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 
     496                                                sat.cand->expr, std::move( compat.env ), std::move( compat.open ),
    486497                                                ast::AssertionSet{} /* need moved into satisfaction state */,
    487498                                                sat.cand->cost, sat.cand->cvtCost );
     
    489500                                        ast::AssertionSet nextNewNeed{ sat.newNeed };
    490501                                        InferCache nextInferred{ sat.inferred };
    491                                        
     502
    492503                                        CostVec nextCosts{ sat.costs };
    493504                                        nextCosts.back() += compat.cost;
    494                                                                
     505
    495506                                        ast::SymbolTable nextSymtab{ sat.symtab };
    496507
     
    501512                                                nextNewNeed.insert( match.need.begin(), match.need.end() );
    502513
    503                                                 bindAssertion( r.decl, r.info, nextCand, match, nextInferred );
     514                                                bindAssertion( r.expr, r.info, nextCand, match, nextInferred );
    504515                                        }
    505516
    506517                                        // either add successful match or push back next state
    507518                                        if ( nextNewNeed.empty() ) {
    508                                                 finalizeAssertions( 
     519                                                finalizeAssertions(
    509520                                                        nextCand, nextInferred, thresholds, std::move( nextCosts ), out );
    510521                                        } else {
    511                                                 nextSats.emplace_back( 
    512                                                         std::move( nextCand ), std::move( nextNewNeed ), 
    513                                                         std::move( nextInferred ), std::move( nextCosts ), 
     522                                                nextSats.emplace_back(
     523                                                        std::move( nextCand ), std::move( nextNewNeed ),
     524                                                        std::move( nextInferred ), std::move( nextCosts ),
    514525                                                        std::move( nextSymtab ) );
    515526                                        }
     
    523534                nextSats.clear();
    524535        }
    525        
     536
    526537        // exceeded recursion limit if reaches here
    527538        if ( out.empty() ) {
  • src/ResolvExpr/Unify.cc

    r42f6e07 r2b4daf2  
    773773
    774774                        const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {
    775                                 if ( const ast::EqvClass * clz = tenv.lookup( typeInst->name ) ) {
     775                                if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) {
    776776                                        // expand ttype parameter into its actual type
    777777                                        if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) {
     
    811811                /// Creates a tuple type based on a list of DeclWithType
    812812                template< typename Iter >
    813                 static ast::ptr< ast::Type > tupleFromTypes( Iter crnt, Iter end ) {
     813                static const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {
    814814                        std::vector< ast::ptr< ast::Type > > types;
    815815                        while ( crnt != end ) {
     
    821821                        }
    822822
    823                         return { new ast::TupleType{ std::move(types) } };
     823                        return new ast::TupleType{ std::move(types) };
    824824                }
    825825
     
    888888                }
    889889
    890                 static void markAssertionSet( ast::AssertionSet & assns, const ast::DeclWithType * assn ) {
     890                static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) {
    891891                        auto i = assns.find( assn );
    892892                        if ( i != assns.end() ) {
     
    900900                        const ast::FunctionType * type
    901901                ) {
    902                         for ( const auto & tyvar : type->forall ) {
    903                                 for ( const ast::DeclWithType * assert : tyvar->assertions ) {
    904                                         markAssertionSet( assn1, assert );
    905                                         markAssertionSet( assn2, assert );
    906                                 }
     902                        for ( auto & assert : type->assertions ) {
     903                                markAssertionSet( assn1, assert );
     904                                markAssertionSet( assn2, assert );
    907905                        }
    908906                }
     
    10301028
    10311029                void postvisit( const ast::TypeInstType * typeInst ) {
    1032                         assert( open.find( typeInst->name ) == open.end() );
     1030                        assert( open.find( *typeInst ) == open.end() );
    10331031                        handleRefType( typeInst, type2 );
    10341032                }
     
    10361034        private:
    10371035                /// Creates a tuple type based on a list of Type
    1038                 static ast::ptr< ast::Type > tupleFromTypes(
     1036                static const ast::Type * tupleFromTypes(
    10391037                        const std::vector< ast::ptr< ast::Type > > & tys
    10401038                ) {
     
    11711169                auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 );
    11721170                ast::OpenVarSet::const_iterator
    1173                         entry1 = var1 ? open.find( var1->name ) : open.end(),
    1174                         entry2 = var2 ? open.find( var2->name ) : open.end();
     1171                        entry1 = var1 ? open.find( *var1 ) : open.end(),
     1172                        entry2 = var2 ? open.find( *var2 ) : open.end();
    11751173                bool isopen1 = entry1 != open.end();
    11761174                bool isopen2 = entry2 != open.end();
  • src/SymTab/Mangler.cc

    r42f6e07 r2b4daf2  
    671671                                        int dcount = 0, fcount = 0, vcount = 0, acount = 0;
    672672                                        mangleName += Encoding::forall;
    673                                         for ( const ast::TypeDecl * decl : ptype->forall ) {
     673                                        for ( auto & decl : ptype->forall ) {
    674674                                                switch ( decl->kind ) {
    675675                                                case ast::TypeDecl::Kind::Dtype:
     
    686686                                                } // switch
    687687                                                varNums[ decl->name ] = std::make_pair( nextVarNum, (int)decl->kind );
    688                                                 for ( const ast::DeclWithType * assert : decl->assertions ) {
    689                                                         ast::Pass<Mangler_new> sub_mangler(
    690                                                                 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums );
    691                                                         assert->accept( sub_mangler );
    692                                                         assertionNames.push_back( sub_mangler.core.get_mangleName() );
    693                                                         acount++;
    694                                                 } // for
     688                                        } // for
     689                                        for ( auto & assert : ptype->assertions ) {
     690                                                ast::Pass<Mangler_new> sub_mangler(
     691                                                        mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums );
     692                                                assert->var->accept( sub_mangler );
     693                                                assertionNames.push_back( sub_mangler.core.get_mangleName() );
     694                                                acount++;
    695695                                        } // for
    696696                                        mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_";
  • src/SymTab/Validate.cc

    r42f6e07 r2b4daf2  
    271271        };
    272272
    273         struct ArrayLength : public WithIndexer {
     273        struct InitializerLength {
    274274                /// for array types without an explicit length, compute the length and store it so that it
    275275                /// is known to the rest of the phases. For example,
     
    282282
    283283                void previsit( ObjectDecl * objDecl );
     284        };
     285
     286        struct ArrayLength : public WithIndexer {
     287                static void computeLength( std::list< Declaration * > & translationUnit );
     288
    284289                void previsit( ArrayType * arrayType );
    285290        };
     
    382387                                        FixObjectType::fix, translationUnit);
    383388                        }
    384                         Stats::Time::TimeCall("Array Length",
    385                                 ArrayLength::computeLength, translationUnit);
     389                        Stats::Time::TimeCall("Initializer Length",
     390                                InitializerLength::computeLength, translationUnit);
     391                        if (!useNewAST) {
     392                                Stats::Time::TimeCall("Array Length",
     393                                        ArrayLength::computeLength, translationUnit);
     394                        }
    386395                        Stats::Time::TimeCall("Find Special Declarations",
    387396                                Validate::findSpecialDecls, translationUnit);
     
    13321341        }
    13331342
     1343        void InitializerLength::computeLength( std::list< Declaration * > & translationUnit ) {
     1344                PassVisitor<InitializerLength> len;
     1345                acceptAll( translationUnit, len );
     1346        }
     1347
    13341348        void ArrayLength::computeLength( std::list< Declaration * > & translationUnit ) {
    13351349                PassVisitor<ArrayLength> len;
     
    13371351        }
    13381352
    1339         void ArrayLength::previsit( ObjectDecl * objDecl ) {
     1353        void InitializerLength::previsit( ObjectDecl * objDecl ) {
    13401354                if ( ArrayType * at = dynamic_cast< ArrayType * >( objDecl->type ) ) {
    13411355                        if ( at->dimension ) return;
     
    13471361
    13481362        void ArrayLength::previsit( ArrayType * type ) {
    1349                 if (!useNewAST) {
    1350                         if ( type->dimension ) {
    1351                                 // need to resolve array dimensions early so that constructor code can correctly determine
    1352                                 // if a type is a VLA (and hence whether its elements need to be constructed)
    1353                                 ResolvExpr::findSingleExpression( type->dimension, Validate::SizeType->clone(), indexer );
    1354 
    1355                                 // must re-evaluate whether a type is a VLA, now that more information is available
    1356                                 // (e.g. the dimension may have been an enumerator, which was unknown prior to this step)
    1357                                 type->isVarLen = ! InitTweak::isConstExpr( type->dimension );
    1358                         }
     1363                if ( type->dimension ) {
     1364                        // need to resolve array dimensions early so that constructor code can correctly determine
     1365                        // if a type is a VLA (and hence whether its elements need to be constructed)
     1366                        ResolvExpr::findSingleExpression( type->dimension, Validate::SizeType->clone(), indexer );
     1367
     1368                        // must re-evaluate whether a type is a VLA, now that more information is available
     1369                        // (e.g. the dimension may have been an enumerator, which was unknown prior to this step)
     1370                        type->isVarLen = ! InitTweak::isConstExpr( type->dimension );
    13591371                }
    13601372        }
     
    14621474                }
    14631475        }
     1476
     1477        /*
    14641478
    14651479        /// Associates forward declarations of aggregates with their definitions
     
    18441858                }
    18451859        };
     1860        */
    18461861} // anonymous namespace
    18471862
     1863/*
    18481864const ast::Type * validateType(
    18491865                const CodeLocation & loc, const ast::Type * type, const ast::SymbolTable & symtab ) {
     
    18541870        return type->accept( lrt )->accept( fpd );
    18551871}
     1872*/
    18561873
    18571874} // namespace SymTab
  • src/Tuples/TupleAssignment.cc

    r42f6e07 r2b4daf2  
    504504
    505505                        std::vector< ast::ptr< ast::Expr > > match() override {
    506                                 // temporary workaround for new and old ast to coexist and avoid name collision
    507                                 static UniqueName lhsNamer( "__massassign_Ln" );
    508                                 static UniqueName rhsNamer( "__massassign_Rn" );
     506                                static UniqueName lhsNamer( "__massassign_L" );
     507                                static UniqueName rhsNamer( "__massassign_R" );
    509508                                // empty tuple case falls into this matcher
    510509                                assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
     
    535534
    536535                        std::vector< ast::ptr< ast::Expr > > match() override {
    537                                 // temporary workaround for new and old ast to coexist and avoid name collision
    538                                 static UniqueName lhsNamer( "__multassign_Ln" );
    539                                 static UniqueName rhsNamer( "__multassign_Rn" );
     536                                static UniqueName lhsNamer( "__multassign_L" );
     537                                static UniqueName rhsNamer( "__multassign_R" );
    540538
    541539                                if ( lhs.size() != rhs.size() ) return {};
  • tests/.expect/KRfunctions.nast.x86.txt

    r42f6e07 r2b4daf2  
    8686    __attribute__ ((unused)) signed int (*_X11_retval_f11PA0i_1)[];
    8787}
    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)];
     88signed 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)];
    9090}
    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)];
     91signed 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)];
    9393}
    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)];
     94signed 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)];
    9696}
    9797signed int _X3f15Fi_iii__1(signed int _X1ai_1, signed int _X1bi_1, signed int _X1ci_1){
  • tests/.expect/attributes.nast.x86.txt

    r42f6e07 r2b4daf2  
    623623__attribute__ ((used,used,used,used)) const signed int *_X3vd3PKi_1;
    624624__attribute__ ((used,used,unused,used,unused)) const signed int *_X3vd4PKi_1;
    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)];
     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)];
    627627__attribute__ ((used,used,used,used)) const signed int (*_X3vd7Fi___1)();
    628628__attribute__ ((used,used,unused,used,used)) const signed int (*_X3vd8Fi___1)();
     
    647647    __attribute__ ((unused,unused,used)) signed int _X2t1i_2;
    648648    __attribute__ ((unused,unused,unused,unused,unused)) signed int **_X2t2PPi_2;
    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)];
     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)];
    651651    __attribute__ ((unused,unused,unused)) signed int _X2t5Fi___2();
    652652    __attribute__ ((unused,unused,unused,unused)) signed int *_X2t6FPi___2();
     
    671671signed int _X4tpr2Fi_PPi__1(__attribute__ ((unused,unused,unused,unused,unused,unused)) signed int **_X3FooPPi_1);
    672672signed 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 long int )5)]));
     673signed int _X4tpr4Fi_Fi_Pi___1(__attribute__ ((unused,unused)) signed int (*__anonymous_object1)(signed int __param_0[((unsigned int )5)]));
    674674signed int _X4tpr5Fi_Fi____1(__attribute__ ((unused,unused,unused)) signed int (*_X3FooFi___1)());
    675675signed int _X4tpr6Fi_Fi____1(__attribute__ ((unused,unused,unused)) signed int (*_X3FooFi___1)());
     
    679679    __attribute__ ((used,unused)) signed int _X3ad1i_2;
    680680    __attribute__ ((unused,unused,unused)) signed int *_X3ad2Pi_2;
    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)];
     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)];
    683683    __attribute__ ((unused,unused,unused,unused,used)) signed int _X3ad5i_2;
    684684    __attribute__ ((unused,unused,unused,unused,unused)) signed int _X3ad6Fi___2();
  • tests/.expect/functions.nast.x86.txt

    r42f6e07 r2b4daf2  
    4646    __attribute__ ((unused)) signed int (*_X11_retval_f10PA0i_1)[];
    4747}
    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)];
     48signed int (*_X3f11FPA0A0i___1())[][((unsigned int )3)]{
     49    __attribute__ ((unused)) signed int (*_X11_retval_f11PA0A0i_1)[][((unsigned int )3)];
     50}
     51signed int (*_X3f12FPA0A0i___1())[][((unsigned int )3)]{
     52    __attribute__ ((unused)) signed int (*_X11_retval_f12PA0A0i_1)[][((unsigned int )3)];
    5353}
    5454signed int _X4fII1Fi_i__1(signed int _X1ii_1){
     
    250250signed 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)){
    251251    __attribute__ ((unused)) signed int _X9_retval_fi_1;
    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)];
     252    signed int (*(*_X2pcPA0A0PA0A0i_2)[][((unsigned int )10)])[][((unsigned int )3)];
     253    signed int (*(*_X1pPA0A0PA0A0i_2)[][((unsigned int )10)])[][((unsigned int )3)];
    254254    signed int (*(*_X1pPA0Fi_i__2)[])(signed int __param_0);
    255255}
  • tests/Makefile.am

    r42f6e07 r2b4daf2  
    103103
    104104mostlyclean-local :
     105        find ${builddir} -not -path './__pycache__/*' -path '*.o' -delete
     106        find ${builddir} -not -path './__pycache__/*' -path '*/.err/*.log' -delete
     107        find ${builddir} -not -path './__pycache__/*' -path '*/.out/*.log' -delete
    105108        rm -f ${EXTRA_PROGRAMS}
    106109        rm -rf __pycache__
    107         find ${builddir} -path '*.o' -delete
    108         find ${builddir} -path '*/.err/*.log' -delete
    109         find ${builddir} -path '*/.out/*.log' -delete
    110110
    111111distclean-local :
  • tests/concurrent/futures/.expect/basic.txt

    r42f6e07 r2b4daf2  
     1start
    12done
  • tests/concurrent/futures/basic.cfa

    r42f6e07 r2b4daf2  
    11#include <thread.hfa>
     2
    23enum {NFUTURES = 10};
    34
     
    8384
    8485int main() {
     86        printf( "start\n" );                            // non-empty .expect file
    8587        processor procs[2];
    8688        {
  • tests/errors/.expect/completeType.nast.x64.txt

    r42f6e07 r2b4daf2  
    1212      Application of
    1313        Variable Expression: *?: forall
    14           DT: data type
     14          instance of type DT (not function type)
    1515          function
    1616        ... with parameters
     
    2121        ... with resolved type:
    2222          pointer to forall
    23             [unbound]:data type
     23            instance of type [unbound] (not function type)
    2424            function
    2525          ... with parameters
     
    4141    void
    4242  )
    43   Environment:([unbound]) -> instance of struct A without body (no widening)
     43  Environment:([unbound]DT) -> instance of struct A without body (no widening)
    4444
    4545
     
    4747      Application of
    4848        Variable Expression: *?: forall
    49           DT: data type
     49          instance of type DT (not function type)
    5050          function
    5151        ... with parameters
     
    5656        ... with resolved type:
    5757          pointer to forall
    58             [unbound]:data type
     58            instance of type [unbound] (not function type)
    5959            function
    6060          ... with parameters
     
    7676    void
    7777  )
    78   Environment:([unbound]) -> instance of struct B with body (no widening)
     78  Environment:([unbound]DT) -> instance of struct B with body (no widening)
    7979
    8080
     
    113113Cost ( 0, 1, 0, 0, 1, -5, 0 ): Application of
    114114            Variable Expression: baz: forall
    115               T: sized data type
    116               ... with assertions
    117                 ?=?: pointer to function
     115              instance of type T (not function type)
     116              with assertions
     117              Variable Expression: ?=?: pointer to function
     118              ... with parameters
     119                reference to instance of type T (not function type)
     120                instance of type T (not function type)
     121              ... returning
     122                instance of type T (not function type)
     123
     124              ... with resolved type:
     125                pointer to function
    118126                ... with parameters
    119127                  reference to instance of type T (not function type)
     
    122130                  instance of type T (not function type)
    123131
    124                 ?{}: pointer to function
    125                 ... with parameters
    126                   reference to instance of type T (not function type)
    127                 ... returning nothing
    128 
    129                 ?{}: pointer to function
    130                 ... with parameters
    131                   reference to instance of type T (not function type)
    132                   instance of type T (not function type)
    133                 ... returning nothing
    134 
    135                 ^?{}: pointer to function
    136                 ... with parameters
    137                   reference to instance of type T (not function type)
    138                 ... returning nothing
    139 
     132              Variable Expression: ?{}: pointer to function
     133              ... with parameters
     134                reference to instance of type T (not function type)
     135              ... returning nothing
     136
     137              ... with resolved type:
     138                pointer to function
     139                ... with parameters
     140                  reference to instance of type T (not function type)
     141                ... returning nothing
     142
     143              Variable Expression: ?{}: pointer to function
     144              ... with parameters
     145                reference to instance of type T (not function type)
     146                instance of type T (not function type)
     147              ... returning nothing
     148
     149              ... with resolved type:
     150                pointer to function
     151                ... with parameters
     152                  reference to instance of type T (not function type)
     153                  instance of type T (not function type)
     154                ... returning nothing
     155
     156              Variable Expression: ^?{}: pointer to function
     157              ... with parameters
     158                reference to instance of type T (not function type)
     159              ... returning nothing
     160
     161              ... with resolved type:
     162                pointer to function
     163                ... with parameters
     164                  reference to instance of type T (not function type)
     165                ... returning nothing
    140166
    141167              function
     
    146172            ... with resolved type:
    147173              pointer to forall
    148                 [unbound]:sized data type
    149                 ... with assertions
    150                   ?=?: pointer to function
     174                instance of type [unbound] (not function type)
     175                with assertions
     176                Variable Expression: ?=?: pointer to function
     177                ... with parameters
     178                  reference to instance of type T (not function type)
     179                  instance of type T (not function type)
     180                ... returning
     181                  instance of type T (not function type)
     182
     183                ... with resolved type:
     184                  pointer to function
    151185                  ... with parameters
    152186                    reference to instance of type [unbound] (not function type)
     
    155189                    instance of type [unbound] (not function type)
    156190
    157                   ?{}: pointer to function
     191                Variable Expression: ?{}: pointer to function
     192                ... with parameters
     193                  reference to instance of type T (not function type)
     194                ... returning nothing
     195
     196                ... with resolved type:
     197                  pointer to function
    158198                  ... with parameters
    159199                    reference to instance of type [unbound] (not function type)
    160200                  ... returning nothing
    161201
    162                   ?{}: pointer to function
     202                Variable Expression: ?{}: pointer to function
     203                ... with parameters
     204                  reference to instance of type T (not function type)
     205                  instance of type T (not function type)
     206                ... returning nothing
     207
     208                ... with resolved type:
     209                  pointer to function
    163210                  ... with parameters
    164211                    reference to instance of type [unbound] (not function type)
     
    166213                  ... returning nothing
    167214
    168                   ^?{}: pointer to function
     215                Variable Expression: ^?{}: pointer to function
     216                ... with parameters
     217                  reference to instance of type T (not function type)
     218                ... returning nothing
     219
     220                ... with resolved type:
     221                  pointer to function
    169222                  ... with parameters
    170223                    reference to instance of type [unbound] (not function type)
    171224                  ... returning nothing
    172 
    173225
    174226                function
     
    188240          void
    189241        )
    190         Environment:([unbound]) -> instance of type T (not function type) (no widening)
     242        Environment:([unbound]T) -> instance of type T (not function type) (no widening)
    191243
    192244      Could not satisfy assertion:
    193 ?=?: pointer to function
     245Variable Expression: ?=?: pointer to function
    194246        ... with parameters
    195           reference to instance of type [unbound] (not function type)
    196           instance of type [unbound] (not function type)
     247          reference to instance of type T (not function type)
     248          instance of type T (not function type)
    197249        ... returning
    198           instance of type [unbound] (not function type)
    199 
     250          instance of type T (not function type)
     251
     252        ... with resolved type:
     253          pointer to function
     254          ... with parameters
     255            reference to instance of type [unbound] (not function type)
     256            instance of type [unbound] (not function type)
     257          ... returning
     258            instance of type [unbound] (not function type)
     259
  • tests/errors/.expect/completeType.nast.x86.txt

    r42f6e07 r2b4daf2  
    1212      Application of
    1313        Variable Expression: *?: forall
    14           DT: data type
     14          instance of type DT (not function type)
    1515          function
    1616        ... with parameters
     
    2121        ... with resolved type:
    2222          pointer to forall
    23             [unbound]:data type
     23            instance of type [unbound] (not function type)
    2424            function
    2525          ... with parameters
     
    4141    void
    4242  )
    43   Environment:([unbound]) -> instance of struct A without body (no widening)
     43  Environment:([unbound]DT) -> instance of struct A without body (no widening)
    4444
    4545
     
    4747      Application of
    4848        Variable Expression: *?: forall
    49           DT: data type
     49          instance of type DT (not function type)
    5050          function
    5151        ... with parameters
     
    5656        ... with resolved type:
    5757          pointer to forall
    58             [unbound]:data type
     58            instance of type [unbound] (not function type)
    5959            function
    6060          ... with parameters
     
    7676    void
    7777  )
    78   Environment:([unbound]) -> instance of struct B with body (no widening)
     78  Environment:([unbound]DT) -> instance of struct B with body (no widening)
    7979
    8080
     
    113113Cost ( 0, 1, 0, 0, 1, -5, 0 ): Application of
    114114            Variable Expression: baz: forall
    115               T: sized data type
    116               ... with assertions
    117                 ?=?: pointer to function
     115              instance of type T (not function type)
     116              with assertions
     117              Variable Expression: ?=?: pointer to function
     118              ... with parameters
     119                reference to instance of type T (not function type)
     120                instance of type T (not function type)
     121              ... returning
     122                instance of type T (not function type)
     123
     124              ... with resolved type:
     125                pointer to function
    118126                ... with parameters
    119127                  reference to instance of type T (not function type)
     
    122130                  instance of type T (not function type)
    123131
    124                 ?{}: pointer to function
    125                 ... with parameters
    126                   reference to instance of type T (not function type)
    127                 ... returning nothing
    128 
    129                 ?{}: pointer to function
    130                 ... with parameters
    131                   reference to instance of type T (not function type)
    132                   instance of type T (not function type)
    133                 ... returning nothing
    134 
    135                 ^?{}: pointer to function
    136                 ... with parameters
    137                   reference to instance of type T (not function type)
    138                 ... returning nothing
    139 
     132              Variable Expression: ?{}: pointer to function
     133              ... with parameters
     134                reference to instance of type T (not function type)
     135              ... returning nothing
     136
     137              ... with resolved type:
     138                pointer to function
     139                ... with parameters
     140                  reference to instance of type T (not function type)
     141                ... returning nothing
     142
     143              Variable Expression: ?{}: pointer to function
     144              ... with parameters
     145                reference to instance of type T (not function type)
     146                instance of type T (not function type)
     147              ... returning nothing
     148
     149              ... with resolved type:
     150                pointer to function
     151                ... with parameters
     152                  reference to instance of type T (not function type)
     153                  instance of type T (not function type)
     154                ... returning nothing
     155
     156              Variable Expression: ^?{}: pointer to function
     157              ... with parameters
     158                reference to instance of type T (not function type)
     159              ... returning nothing
     160
     161              ... with resolved type:
     162                pointer to function
     163                ... with parameters
     164                  reference to instance of type T (not function type)
     165                ... returning nothing
    140166
    141167              function
     
    146172            ... with resolved type:
    147173              pointer to forall
    148                 [unbound]:sized data type
    149                 ... with assertions
    150                   ?=?: pointer to function
     174                instance of type [unbound] (not function type)
     175                with assertions
     176                Variable Expression: ?=?: pointer to function
     177                ... with parameters
     178                  reference to instance of type T (not function type)
     179                  instance of type T (not function type)
     180                ... returning
     181                  instance of type T (not function type)
     182
     183                ... with resolved type:
     184                  pointer to function
    151185                  ... with parameters
    152186                    reference to instance of type [unbound] (not function type)
     
    155189                    instance of type [unbound] (not function type)
    156190
    157                   ?{}: pointer to function
     191                Variable Expression: ?{}: pointer to function
     192                ... with parameters
     193                  reference to instance of type T (not function type)
     194                ... returning nothing
     195
     196                ... with resolved type:
     197                  pointer to function
    158198                  ... with parameters
    159199                    reference to instance of type [unbound] (not function type)
    160200                  ... returning nothing
    161201
    162                   ?{}: pointer to function
     202                Variable Expression: ?{}: pointer to function
     203                ... with parameters
     204                  reference to instance of type T (not function type)
     205                  instance of type T (not function type)
     206                ... returning nothing
     207
     208                ... with resolved type:
     209                  pointer to function
    163210                  ... with parameters
    164211                    reference to instance of type [unbound] (not function type)
     
    166213                  ... returning nothing
    167214
    168                   ^?{}: pointer to function
     215                Variable Expression: ^?{}: pointer to function
     216                ... with parameters
     217                  reference to instance of type T (not function type)
     218                ... returning nothing
     219
     220                ... with resolved type:
     221                  pointer to function
    169222                  ... with parameters
    170223                    reference to instance of type [unbound] (not function type)
    171224                  ... returning nothing
    172 
    173225
    174226                function
     
    188240          void
    189241        )
    190         Environment:([unbound]) -> instance of type T (not function type) (no widening)
     242        Environment:([unbound]T) -> instance of type T (not function type) (no widening)
    191243
    192244      Could not satisfy assertion:
    193 ?=?: pointer to function
     245Variable Expression: ?=?: pointer to function
    194246        ... with parameters
    195           reference to instance of type [unbound] (not function type)
    196           instance of type [unbound] (not function type)
     247          reference to instance of type T (not function type)
     248          instance of type T (not function type)
    197249        ... returning
    198           instance of type [unbound] (not function type)
    199 
     250          instance of type T (not function type)
     251
     252        ... with resolved type:
     253          pointer to function
     254          ... with parameters
     255            reference to instance of type [unbound] (not function type)
     256            instance of type [unbound] (not function type)
     257          ... returning
     258            instance of type [unbound] (not function type)
     259
  • tests/raii/.expect/ctor-autogen-ERR1.nast.txt

    r42f6e07 r2b4daf2  
    7070            ... with environment:
    7171              Types:
    72               Non-types:
    7372
    7473
Note: See TracChangeset for help on using the changeset viewer.