Changes in / [92538ab:4559b34]


Ignore:
Files:
7 added
135 deleted
127 edited

Legend:

Unmodified
Added
Removed
  • Jenkinsfile

    r92538ab r4559b34  
    108108
    109109                        // Configure libcfa
    110                         sh 'make -j $(nproc) --no-print-directory configure-libcfa'
     110                        sh 'make -j 8 --no-print-directory configure-libcfa'
    111111                }
    112112        }
     
    116116                dir (BuildDir) {
    117117                        // Build driver
    118                         sh 'make -j $(nproc) --no-print-directory -C driver'
     118                        sh 'make -j 8 --no-print-directory -C driver'
    119119
    120120                        // Build translator
    121                         sh 'make -j $(nproc) --no-print-directory -C src'
     121                        sh 'make -j 8 --no-print-directory -C src'
    122122                }
    123123        }
     
    126126                // Build outside of the src tree to ease cleaning
    127127                dir (BuildDir) {
    128                         sh "make -j \$(nproc) --no-print-directory -C libcfa/${Settings.Architecture.name}-debug"
     128                        sh "make -j 8 --no-print-directory -C libcfa/${Settings.Architecture.name}-debug"
    129129                }
    130130        }
     
    133133                // Build outside of the src tree to ease cleaning
    134134                dir (BuildDir) {
    135                         sh "make -j \$(nproc) --no-print-directory -C libcfa/${Settings.Architecture.name}-nodebug"
     135                        sh "make -j 8 --no-print-directory -C libcfa/${Settings.Architecture.name}-nodebug"
    136136                }
    137137        }
     
    140140                // Build outside of the src tree to ease cleaning
    141141                dir (BuildDir) {
    142                         sh 'make -j $(nproc) --no-print-directory install'
     142                        sh "make -j 8 --no-print-directory install"
    143143                }
    144144        }
     
    161161                Tools.BuildStage('Test: full', Settings.RunAllTests) {
    162162                        dir (BuildDir) {
    163                                         jopt = '-j $(nproc)'
     163                                        jopt = ""
    164164                                        if( Settings.Architecture.node == 'x86' ) {
    165                                                 jopt = '-j2'
     165                                                jopt = "-j2"
    166166                                        }
    167167                                        //Run the tests from the tests directory
  • benchmark/io/http/main.cfa

    r92538ab r4559b34  
    3333//============================================================================================='
    3434
    35 thread StatsPrinter {
    36         Worker * workers;
    37         int worker_cnt;
    38 };
     35thread StatsPrinter {};
    3936
    4037void ?{}( StatsPrinter & this, cluster & cl ) {
    4138        ((thread&)this){ "Stats Printer Thread", cl };
    42         this.worker_cnt = 0;
    4339}
    4440
    4541void ^?{}( StatsPrinter & mutex this ) {}
    46 
    47 #define eng3(X) (ws(3, 3, unit(eng( X ))))
    4842
    4943void main(StatsPrinter & this) {
     
    5751
    5852                print_stats_now( *active_cluster(), CFA_STATS_READY_Q | CFA_STATS_IO );
    59                 if(this.worker_cnt != 0) {
    60                         uint64_t tries = 0;
    61                         uint64_t calls = 0;
    62                         uint64_t header = 0;
    63                         uint64_t splcin = 0;
    64                         uint64_t splcot = 0;
    65                         struct {
    66                                 volatile uint64_t calls;
    67                                 volatile uint64_t bytes;
    68                         } avgrd[zipf_cnts];
    69                         memset(avgrd, 0, sizeof(avgrd));
    70 
    71                         for(i; this.worker_cnt) {
    72                                 tries += this.workers[i].stats.sendfile.tries;
    73                                 calls += this.workers[i].stats.sendfile.calls;
    74                                 header += this.workers[i].stats.sendfile.header;
    75                                 splcin += this.workers[i].stats.sendfile.splcin;
    76                                 splcot += this.workers[i].stats.sendfile.splcot;
    77                                 for(j; zipf_cnts) {
    78                                         avgrd[j].calls += this.workers[i].stats.sendfile.avgrd[j].calls;
    79                                         avgrd[j].bytes += this.workers[i].stats.sendfile.avgrd[j].bytes;
    80                                 }
    81                         }
    82 
    83                         double ratio = ((double)tries) / calls;
    84 
    85                         sout | "----- Worker Stats -----";
    86                         sout | "sendfile  : " | calls | "calls," | tries | "tries (" | ratio | " try/call)";
    87                         sout | "            " | header | "header," | splcin | "splice in," | splcot | "splice out";
    88                         sout | " - zipf sizes:";
    89                         for(i; zipf_cnts) {
    90                                 double written = avgrd[i].calls > 0 ? ((double)avgrd[i].bytes) / avgrd[i].calls : 0;
    91                                 sout | "        " | zipf_sizes[i] | "bytes," | avgrd[i].calls | "shorts," | written | "written";
    92                         }
    93                 }
    94                 else {
    95                         sout | "No Workers!";
    96                 }
    9753        }
    9854}
     
    262218                        {
    263219                                Worker * workers = anew(options.clopts.nworkers);
    264                                 cl[0].prnt->workers = workers;
    265                                 cl[0].prnt->worker_cnt = options.clopts.nworkers;
    266220                                for(i; options.clopts.nworkers) {
    267221                                        // if( options.file_cache.fixed_fds ) {
     
    357311        }
    358312}
    359 
    360 const size_t zipf_sizes[] = { 102, 204, 307, 409, 512, 614, 716, 819, 921, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216, 10240, 20480, 30720, 40960, 51200, 61440, 71680, 81920, 92160, 102400, 204800, 307200, 409600, 512000, 614400, 716800, 819200, 921600 };
    361 static_assert(zipf_cnts == sizeof(zipf_sizes) / sizeof(zipf_sizes[0]));
  • benchmark/io/http/parhttperf

    r92538ab r4559b34  
    66
    77mkdir -p out
    8 rm out/*
    9 echo "httperf --client [0-$(($NTHREADS - 1))]/$NTHREADS $@ > out/result.[0-$(($NTHREADS - 1))].out"
     8rm -v out/*
    109for ((i=0; i<$NTHREADS; i++))
    1110do
     11        # echo "httperf --client $i/$NTHREADS $@ > out/result.$i.out"
    1212        httperf --client $i/$NTHREADS $@ > out/result.$i.out &
    1313done
  • benchmark/io/http/protocol.cfa

    r92538ab r4559b34  
    2424
    2525#include "options.hfa"
    26 #include "worker.hfa"
    2726
    2827#define PLAINTEXT_1WRITE
     
    157156
    158157                count -= ret;
     158                offset += ret;
    159159                size_t in_pipe = ret;
    160160                SPLICE2: while(in_pipe > 0) {
     
    173173}
    174174
     175static void zero_sqe(struct io_uring_sqe * sqe) {
     176        sqe->flags = 0;
     177        sqe->ioprio = 0;
     178        sqe->fd = 0;
     179        sqe->off = 0;
     180        sqe->addr = 0;
     181        sqe->len = 0;
     182        sqe->fsync_flags = 0;
     183        sqe->__pad2[0] = 0;
     184        sqe->__pad2[1] = 0;
     185        sqe->__pad2[2] = 0;
     186        sqe->fd = 0;
     187        sqe->off = 0;
     188        sqe->addr = 0;
     189        sqe->len = 0;
     190}
     191
    175192enum FSM_STATE {
    176193        Initial,
     
    249266}
    250267
    251 static inline int wait_and_process(header_g & this, sendfile_stats_t & stats) {
     268static inline int wait_and_process(header_g & this) {
    252269        wait(this.f);
    253270
     
    278295        }
    279296
    280         stats.header++;
    281 
    282297        // It must be a Short read
    283298        this.len  -= this.f.result;
     
    291306        io_future_t f;
    292307        int fd; int pipe; size_t len; off_t off;
    293         short zipf_idx;
    294308        FSM_Result res;
    295309};
     
    300314        this.len = len;
    301315        this.off = 0;
    302         this.zipf_idx = -1;
    303         STATS: for(i; zipf_cnts) {
    304                 if(len <= zipf_sizes[i]) {
    305                         this.zipf_idx = i;
    306                         break STATS;
    307                 }
    308         }
    309         if(this.zipf_idx < 0) mutex(serr) serr | "SPLICE IN" | len | " greated than biggest zipf file";
    310316}
    311317
     
    323329}
    324330
    325 static inline int wait_and_process(splice_in_t & this, sendfile_stats_t & stats ) {
     331static inline int wait_and_process(splice_in_t & this) {
    326332        wait(this.f);
    327333
     
    339345                        return error(this.res, -ECONNRESET);
    340346                }
    341                 mutex(serr) serr | "SPLICE IN got" | error | ", WTF!";
    342                 return error(this.res, -ECONNRESET);
    343347        }
    344348
     
    353357                return done(this.res);
    354358        }
    355 
    356         stats.splcin++;
    357         stats.avgrd[this.zipf_idx].calls++;
    358         stats.avgrd[this.zipf_idx].bytes += this.f.result;
    359359
    360360        // It must be a Short read
     
    398398}
    399399
    400 static inline void wait_and_process(splice_out_g & this, sendfile_stats_t & stats ) {
     400static inline void wait_and_process(splice_out_g & this) {
    401401        wait(this.f);
    402402
     
    414414                        return error(this, -ECONNRESET);
    415415                }
    416                 mutex(serr) serr | "SPLICE OUT got" | error | ", WTF!";
    417                 return error(this, -ECONNRESET);
    418416        }
    419417
     
    430428
    431429SHORT_WRITE:
    432         stats.splcot++;
    433 
    434430        // It must be a Short Write
    435431        this.len -= this.f.result;
     
    438434}
    439435
    440 int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t fsize, sendfile_stats_t & stats ) {
    441         stats.calls++;
     436int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t fsize ) {
    442437        #if defined(LINKED_IO)
    443438                char buffer[512];
     
    448443
    449444                RETRY_LOOP: for() {
    450                         stats.tries++;
    451445                        int have = need(header.res) + need(splice_in.res) + 1;
    452446                        int idx = 0;
     
    467461                        // we may need to kill the connection if it fails
    468462                        // If it already completed, this is a no-op
    469                         wait_and_process(splice_in, stats);
     463                        wait_and_process(splice_in);
    470464
    471465                        if(is_error(splice_in.res)) {
     
    475469
    476470                        // Process the other 2
    477                         wait_and_process(header, stats);
    478                         wait_and_process(splice_out, stats);
     471                        wait_and_process(header);
     472                        wait_and_process(splice_out);
    479473
    480474                        if(is_done(splice_out.res)) {
     
    496490                return len + fsize;
    497491        #else
    498                 stats.tries++;
    499492                int ret = answer_header(fd, fsize);
    500493                if( ret < 0 ) { close(fd); return ret; }
  • benchmark/io/http/protocol.hfa

    r92538ab r4559b34  
    11#pragma once
    2 
    3 struct sendfile_stats_t;
    42
    53enum HttpCode {
     
    2018int answer_plaintext( int fd );
    2119int answer_empty( int fd );
    22 int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t count, struct sendfile_stats_t & );
     20int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t count );
    2321
    2422[HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len);
  • benchmark/io/http/worker.cfa

    r92538ab r4559b34  
    2323        this.pipe[1] = -1;
    2424        this.done = false;
    25 
    26         this.stats.sendfile.calls = 0;
    27         this.stats.sendfile.tries = 0;
    28         this.stats.sendfile.header = 0;
    29         this.stats.sendfile.splcin = 0;
    30         this.stats.sendfile.splcot = 0;
    31         for(i; zipf_cnts) {
    32                 this.stats.sendfile.avgrd[i].calls = 0;
    33                 this.stats.sendfile.avgrd[i].bytes = 0;
    34         }
    3525}
    3626
     
    133123
    134124                        // Send the desired file
    135                         int ret = answer_sendfile( this.pipe, fd, ans_fd, count, this.stats.sendfile );
     125                        int ret = answer_sendfile( this.pipe, fd, ans_fd, count);
    136126                        if( ret == -ECONNRESET ) break REQUEST;
    137127
  • benchmark/io/http/worker.hfa

    r92538ab r4559b34  
    1111//=============================================================================================
    1212
    13 extern const size_t zipf_sizes[];
    14 enum { zipf_cnts = 36, };
    15 
    16 struct sendfile_stats_t {
    17         volatile uint64_t calls;
    18         volatile uint64_t tries;
    19         volatile uint64_t header;
    20         volatile uint64_t splcin;
    21         volatile uint64_t splcot;
    22         struct {
    23                 volatile uint64_t calls;
    24                 volatile uint64_t bytes;
    25         } avgrd[zipf_cnts];
    26 };
    27 
    2813thread Worker {
    2914        int pipe[2];
     
    3318        int flags;
    3419        volatile bool done;
    35         struct {
    36                 sendfile_stats_t sendfile;
    37         } stats;
    3820};
    3921void ?{}( Worker & this);
  • benchmark/plot.py

    r92538ab r4559b34  
    4040}
    4141
    42 def plot(data, x, y, out):
     42def plot(data, x, y):
    4343        fig, ax = plt.subplots()
    4444        colors = itertools.cycle(['#0095e3','#006cb4','#69df00','#0aa000','#fb0300','#e30002','#fd8f00','#ff7f00','#8f00d6','#4b009a','#ffff00','#b13f00'])
     
    6767        ax.yaxis.set_major_formatter( EngFormatter(unit=field_names[y].unit) )
    6868        plt.legend(loc='upper left')
    69         if out:
    70                 plt.savefig(out)
    71         else:
    72                 plt.show()
     69        plt.show()
    7370
    7471
     
    7875        parser = parser = argparse.ArgumentParser(description='Python Script to draw R.M.I.T. results')
    7976        parser.add_argument('-f', '--file', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
    80         parser.add_argument('-o', '--out', nargs='?', type=str, default=None)
    81         parser.add_argument('-y', nargs='?', type=str, default="")
    8277
    8378        try:
     
    108103                        fields.add(label)
    109104
    110         if not options.out :
    111                 print(series)
    112                 print("fields")
    113                 for f in fields:
    114                         print("{}".format(f))
     105        print(series)
     106        print("fields")
     107        for f in fields:
     108                print("{}".format(f))
    115109
    116         if options.y and options.y in field_names.keys():
    117                 plot(data, "Number of processors", options.y, options.out)
    118         else:
    119                 if options.y:
    120                         print("Could not find key '{}', defaulting to 'ns per ops'".format(options.y))
    121                 plot(data, "Number of processors", "ns per ops", options.out)
     110        plot(data, "Number of processors", "ns per ops")
  • doc/LaTeXmacros/common.sty

    r92538ab r4559b34  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sat Apr  2 17:35:23 2022
    14 %% Update Count     : 570
     13%% Last Modified On : Mon Feb  7 23:00:46 2022
     14%% Update Count     : 569
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    301301  {=>}{$\Rightarrow$}2
    302302  {->}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.075ex}}}\kern-0.2ex\textgreater}2,
     303defaultdialect={CFA},
    303304}% lstset
    304305}% CFAStyle
  • doc/LaTeXmacros/common.tex

    r92538ab r4559b34  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sat Apr  2 16:42:31 2022
    14 %% Update Count     : 553
     13%% Last Modified On : Mon Feb  7 23:00:08 2022
     14%% Update Count     : 552
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    306306  {=>}{$\Rightarrow$}2
    307307  {->}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.075ex}}}\kern-0.2ex\textgreater}2,
     308defaultdialect={CFA},
    308309}% lstset
    309310}% CFAStyle
  • doc/theses/mubeen_zulfiqar_MMath/Makefile

    r92538ab r4559b34  
    1 # Configuration variables
    2 
    3 Build = build
    4 Figures = figures
    5 Pictures = pictures
    6 
    7 TeXSRC = ${wildcard *.tex}
    8 FigSRC = ${notdir ${wildcard ${Figures}/*.fig}}
    9 PicSRC = ${notdir ${wildcard ${Pictures}/*.fig}}
    10 BibSRC = ${wildcard *.bib}
    11 
    12 TeXLIB = .:../../LaTeXmacros:${Build}:          # common latex macros
    13 BibLIB = .:../../bibliography                   # common citation repository
     1DOC = uw-ethesis.pdf
     2BASE = ${DOC:%.pdf=%} # remove suffix
     3# directory for latex clutter files
     4BUILD = build
     5TEXSRC = $(wildcard *.tex)
     6FIGSRC = $(wildcard *.fig)
     7BIBSRC = $(wildcard *.bib)
     8TEXLIB = .:../../LaTeXmacros:${BUILD}: # common latex macros
     9BIBLIB = .:../../bibliography # common citation repository
    1410
    1511MAKEFLAGS = --no-print-directory # --silent
    16 VPATH = ${Build} ${Figures} ${Pictures} # extra search path for file names used in document
     12VPATH = ${BUILD}
    1713
    18 DOCUMENT = uw-ethesis.pdf
    19 BASE = ${basename ${DOCUMENT}}                  # remove suffix
     14### Special Rules:
    2015
    21 # Commands
     16.PHONY: all clean
     17.PRECIOUS: %.dvi %.ps # do not delete intermediate files
    2218
    23 LaTeX = TEXINPUTS=${TeXLIB} && export TEXINPUTS && latex -halt-on-error -output-directory=${Build}
    24 BibTeX = BIBINPUTS=${BibLIB} && export BIBINPUTS && bibtex
    25 #Glossary = INDEXSTYLE=${Build} makeglossaries-lite
     19### Commands:
     20LATEX = TEXINPUTS=${TEXLIB} && export TEXINPUTS && latex -halt-on-error -output-directory=${BUILD}
     21BIBTEX = BIBINPUTS=${BIBLIB} bibtex
     22#GLOSSARY = INDEXSTYLE=${BUILD} makeglossaries-lite
    2623
    27 # Rules and Recipes
     24### Rules and Recipes:
    2825
    29 .PHONY : all clean                              # not file names
    30 .PRECIOUS: %.dvi %.ps # do not delete intermediate files
    31 .ONESHELL :
     26all: ${DOC}
    3227
    33 all : ${DOCUMENT}
     28${BUILD}/%.dvi: ${TEXSRC} ${FIGSRC:%.fig=%.tex} ${BIBSRC} Makefile | ${BUILD}
     29        ${LATEX} ${BASE}
     30        ${BIBTEX} ${BUILD}/${BASE}
     31        ${LATEX} ${BASE}
     32#       ${GLOSSARY} ${BUILD}/${BASE}
     33#       ${LATEX} ${BASE}
    3434
    35 clean :
    36         @rm -frv ${DOCUMENT} ${Build}
     35${BUILD}:
     36        mkdir $@
    3737
    38 # File Dependencies
    39 
    40 %.dvi : ${TeXSRC} ${FigSRC:%.fig=%.tex} ${PicSRC:%.fig=%.pstex} ${BibSRC} Makefile | ${Build}
    41         ${LaTeX} ${BASE}
    42         ${BibTeX} ${Build}/${BASE}
    43         ${LaTeX} ${BASE}
    44         # if needed, run latex again to get citations
    45         if fgrep -s "LaTeX Warning: Citation" ${basename $@}.log ; then ${LaTeX} ${BASE} ; fi
    46 #       ${Glossary} ${Build}/${BASE}
    47 #       ${LaTeX} ${BASE}
    48 
    49 ${Build}:
    50         mkdir -p $@
    51 
    52 %.pdf : ${Build}/%.ps | ${Build}
     38%.pdf : ${BUILD}/%.ps | ${BUILD}
    5339        ps2pdf $<
    5440
    55 %.ps : %.dvi | ${Build}
     41%.ps : %.dvi | ${BUILD}
    5642        dvips $< -o $@
    5743
    58 %.tex : %.fig | ${Build}
    59         fig2dev -L eepic $< > ${Build}/$@
     44%.tex : %.fig | ${BUILD}
     45        fig2dev -L eepic $< > ${BUILD}/$@
    6046
    61 %.ps : %.fig | ${Build}
    62         fig2dev -L ps $< > ${Build}/$@
     47%.ps : %.fig | ${BUILD}
     48        fig2dev -L ps $< > ${BUILD}/$@
    6349
    64 %.pstex : %.fig | ${Build}
    65         fig2dev -L pstex $< > ${Build}/$@
    66         fig2dev -L pstex_t -p ${Build}/$@ $< > ${Build}/$@_t
     50%.pstex : %.fig | ${BUILD}
     51        fig2dev -L pstex $< > ${BUILD}/$@
     52        fig2dev -L pstex_t -p ${BUILD}/$@ $< > ${BUILD}/$@_t
     53
     54clean:
     55        @rm -frv ${DOC} ${BUILD} *.fig.bak
  • doc/theses/mubeen_zulfiqar_MMath/allocator.tex

    r92538ab r4559b34  
    11\chapter{Allocator}
    22
    3 \section{uHeap}
    4 uHeap is a lightweight memory allocator. The objective behind uHeap is to design a minimal concurrent memory allocator that has new features and also fulfills GNU C Library requirements (FIX ME: cite requirements).
    5 
    6 The objective of uHeap's new design was to fulfill following requirements:
    7 \begin{itemize}
    8 \item It should be concurrent and thread-safe for multi-threaded programs.
     3\noindent
     4====================
     5
     6Writing Points:
     7\begin{itemize}
     8\item
     9Objective of uHeapLmmm.
     10\item
     11Design philosophy.
     12\item
     13Background and previous design of uHeapLmmm.
     14\item
     15Distributed design of uHeapLmmm.
     16
     17----- SHOULD WE GIVE IMPLEMENTATION DETAILS HERE? -----
     18
     19\PAB{Maybe. There might be an Implementation chapter.}
     20\item
     21figure.
     22\item
     23Advantages of distributed design.
     24\end{itemize}
     25
     26The new features added to uHeapLmmm (incl. @malloc\_size@ routine)
     27\CFA alloc interface with examples.
     28
     29\begin{itemize}
     30\item
     31Why did we need it?
     32\item
     33The added benefits.
     34\end{itemize}
     35
     36
     37%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     38%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     39%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% uHeapLmmm Design
     40%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     41%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     42
     43\section{Objective of uHeapLmmm}
     44UHeapLmmm is a lightweight memory allocator. The objective behind uHeapLmmm is to design a minimal concurrent memory allocator that has new features and also fulfills GNU C Library requirements (FIX ME: cite requirements).
     45
     46\subsection{Design philosophy}
     47The objective of uHeapLmmm's new design was to fulfill following requirements:
     48\begin{itemize}
     49\item It should be concurrent to be used in multi-threaded programs.
    950\item It should avoid global locks, on resources shared across all threads, as much as possible.
    1051\item It's performance (FIX ME: cite performance benchmarks) should be comparable to the commonly used allocators (FIX ME: cite common allocators).
     
    1455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1556
    16 \section{Design choices for uHeap}
    17 uHeap's design was reviewed and changed to fulfill new requirements (FIX ME: cite allocator philosophy). For this purpose, following two designs of uHeapLmm were proposed:
    18 
    19 \paragraph{Design 1: Centralized}
     57\section{Background and previous design of uHeapLmmm}
     58uHeapLmmm was originally designed by X in X (FIX ME: add original author after confirming with Peter).
     59(FIX ME: make and add figure of previous design with description)
     60
     61%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     62
     63\section{Distributed design of uHeapLmmm}
     64uHeapLmmm's design was reviewed and changed to fulfill new requirements (FIX ME: cite allocator philosophy). For this purpose, following two designs of uHeapLmm were proposed:
     65
     66\paragraph{Design 1: Decentralized}
     67Fixed number of heaps: shard the heap into N heaps each with a bump-area allocated from the @sbrk@ area.
     68Kernel threads (KT) are assigned to the N heaps.
     69When KTs $\le$ N, the heaps are uncontented.
     70When KTs $>$ N, the heaps are contented.
     71By adjusting N, this approach reduces storage at the cost of speed due to contention.
     72In all cases, a thread acquires/releases a lock, contented or uncontented.
     73\begin{cquote}
     74\centering
     75\input{AllocDS1}
     76\end{cquote}
     77Problems: need to know when a KT is created and destroyed to know when to assign/un-assign a heap to the KT.
     78
     79\paragraph{Design 2: Centralized}
    2080One heap, but lower bucket sizes are N-shared across KTs.
    2181This design leverages the fact that 95\% of allocation requests are less than 512 bytes and there are only 3--5 different request sizes.
     
    3090When no thread is assigned a bucket number, its free storage is unavailable. All KTs will be contended for one lock on sbrk for their initial allocations (before free-lists gets populated).
    3191
    32 \paragraph{Design 2: Decentralized N Heaps}
    33 Fixed number of heaps: shard the heap into N heaps each with a bump-area allocated from the @sbrk@ area.
    34 Kernel threads (KT) are assigned to the N heaps.
    35 When KTs $\le$ N, the heaps are uncontented.
    36 When KTs $>$ N, the heaps are contented.
    37 By adjusting N, this approach reduces storage at the cost of speed due to contention.
    38 In all cases, a thread acquires/releases a lock, contented or uncontented.
    39 \begin{cquote}
    40 \centering
    41 \input{AllocDS1}
    42 \end{cquote}
    43 Problems: need to know when a KT is created and destroyed to know when to assign/un-assign a heap to the KT.
    44 
    45 \paragraph{Design 3: Decentralized Per-thread Heaps}
    46 Design 3 is similar to design 2 but instead of having an M:N model, it uses a 1:1 model. So, instead of having N heaos and sharing them among M KTs, Design 3 has one heap for each KT.
    47 Dynamic number of heaps: create a thread-local heap for each kernel thread (KT) with a bump-area allocated from the @sbrk@ area.
    48 Each KT will have its own exclusive thread-local heap. Heap will be uncontended between KTs regardless how many KTs have been created.
    49 Operations on @sbrk@ area will still be protected by locks.
    50 %\begin{cquote}
    51 %\centering
    52 %\input{AllocDS3} FIXME add figs
    53 %\end{cquote}
    54 Problems: We cannot destroy the heap when a KT exits because our dynamic objects have ownership and they are returned to the heap that created them when the program frees a dynamic object. All dynamic objects point back to their owner heap. If a thread A creates an object O, passes it to another thread B, and A itself exits. When B will free object O, O should return to A's heap so A's heap should be preserved for the lifetime of the whole program as their might be objects in-use of other threads that were allocated by A. Also, we need to know when a KT is created and destroyed to know when to create/destroy a heap for the KT.
    55 
    56 \paragraph{Design 4: Decentralized Per-CPU Heaps}
    57 Design 4 is similar to Design 3 but instead of having a heap for each thread, it creates a heap for each CPU.
    58 Fixed number of heaps for a machine: create a heap for each CPU with a bump-area allocated from the @sbrk@ area.
    59 Each CPU will have its own CPU-local heap. When the program does a dynamic memory operation, it will be entertained by the heap of the CPU where the process is currently running on.
    60 Each CPU will have its own exclusive heap. Just like Design 3(FIXME cite), heap will be uncontended between KTs regardless how many KTs have been created.
    61 Operations on @sbrk@ area will still be protected by locks.
    62 To deal with preemtion during a dynamic memory operation, librseq(FIXME cite) will be used to make sure that the whole dynamic memory operation completes on one CPU. librseq's restartable sequences can make it possible to re-run a critical section and undo the current writes if a preemption happened during the critical section's execution.
    63 %\begin{cquote}
    64 %\centering
    65 %\input{AllocDS4} FIXME add figs
    66 %\end{cquote}
    67 
    68 Problems: This approach was slower than the per-thread model. Also, librseq does not provide such restartable sequences to detect preemtions in user-level threading system which is important to us as CFA(FIXME cite) has its own threading system that we want to support.
    69 
    70 Out of the four designs, Design 3 was chosen because of the following reasons.
    71 \begin{itemize}
    72 \item
    73 Decentralized designes are better in general as compared to centralized design because their concurrency is better across all bucket-sizes as design 1 shards a few buckets of selected sizes while other designs shards all the buckets. Decentralized designes shard the whole heap which has all the buckets with the addition of sharding sbrk area. So Design 1 was eliminated.
    74 \item
    75 Design 2 was eliminated because it has a possibility of contention in-case of KT > N while Design 3 and 4 have no contention in any scenerio.
    76 \item
    77 Design 4 was eliminated because it was slower than Design 3 and it provided no way to achieve user-threading safety using librseq. We had to use CFA interruption handling to achive user-threading safety which has some cost to it. Desing 4 was already slower than Design 3, adding cost of interruption handling on top of that would have made it even slower.
    78 \end{itemize}
    79 
     92Out of the two designs, Design 1 was chosen because it's concurrency is better across all bucket-sizes as design-2 shards a few buckets of selected sizes while design-1 shards all the buckets. Design-2 shards the whole heap which has all the buckets with the addition of sharding sbrk area.
    8093
    8194\subsection{Advantages of distributed design}
    82 
    83 The distributed design of uHeap is concurrent to work in multi-threaded applications.
    84 
    85 Some key benefits of the distributed design of uHeap are as follows:
    86 
    87 \begin{itemize}
    88 \item
    89 The bump allocation is concurrent as memory taken from sbrk is sharded across all heaps as bump allocation reserve. The call to sbrk will be protected using locks but bump allocation (on memory taken from sbrk) will not be contended once the sbrk call has returned.
    90 \item
    91 Low or almost no contention on heap resources.
     95The distributed design of uHeapLmmm is concurrent to work in multi-threaded applications.
     96
     97Some key benefits of the distributed design of uHeapLmmm are as follows:
     98
     99\begin{itemize}
     100\item
     101The bump allocation is concurrent as memory taken from sbrk is sharded across all heaps as bump allocation reserve. The lock on bump allocation (on memory taken from sbrk) will only be contended if KTs > N. The contention on sbrk area is less likely as it will only happen in the case if heaps assigned to two KTs get short of bump allocation reserve simultanously.
     102\item
     103N heaps are created at the start of the program and destroyed at the end of program. When a KT is created, we only assign it to one of the heaps. When a KT is destroyed, we only dissociate it from the assigned heap but we do not destroy that heap. That heap will go back to our pool-of-heaps, ready to be used by some new KT. And if that heap was shared among multiple KTs (like the case of KTs > N) then, on deletion of one KT, that heap will be still in-use of the other KTs. This will prevent creation and deletion of heaps during run-time as heaps are re-usable which helps in keeping low-memory footprint.
    92104\item
    93105It is possible to use sharing and stealing techniques to share/find unused storage, when a free list is unused or empty.
     
    96108\end{itemize}
    97109
    98 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    99 
    100 \section{uHeap Structure}
    101 
    102 As described in (FIXME cite 2.4) uHeap uses following features of multi-threaded memory allocators.
    103 \begin{itemize}
    104 \item
    105 uHeap has multiple heaps without a global heap and uses 1:1 model. (FIXME cite 2.5 1:1 model)
    106 \item
    107 uHeap uses object ownership. (FIXME cite 2.5.2)
    108 \item
    109 uHeap does not use object containers (FIXME cite 2.6) or any coalescing technique. Instead each dynamic object allocated by uHeap has a header than contains bookkeeping information.
    110 \item
    111 Each thread-local heap in uHeap has its own allocation buffer that is taken from the system using sbrk() call. (FIXME cite 2.7)
    112 \item
    113 Unless a heap is freeing an object that is owned by another thread's heap or heap is using sbrk() system call, uHeap is mostly lock-free which eliminates most of the contention on shared resources. (FIXME cite 2.8)
    114 \end{itemize}
    115 
    116 As uHeap uses a heap per-thread model to reduce contention on heap resources, we manage a list of heaps (heap-list) that can be used by threads. The list is empty at the start of the program. When a kernel thread (KT) is created, we check if heap-list is empty. If no then a heap is removed from the heap-list and is given to this new KT to use exclusively. If yes then a new heap object is created in dynamic memory and is given to this new KT to use exclusively. When a KT exits, its heap is not destroyed but instead its heap is put on the heap-list and is ready to be reused by new KTs.
    117 
    118 This reduces the memory footprint as the objects on free-lists of a KT that has exited can be reused by a new KT. Also, we preserve all the heaps that were created during the lifetime of the program till the end of the program. uHeap uses object ownership where an object is freed to the free-buckets of the heap that allocated it. Even after a KT A has exited, its heap has to be preserved as there might be objects in-use of other threads that were initially allocated by A and the passed to other threads.
    119 
    120 \begin{figure}
    121 \centering
    122 \includegraphics[width=0.65\textwidth]{figures/NewHeapStructure.eps}
    123 \caption{HeapStructure}
    124 \label{fig:heapStructureFig}
    125 \end{figure}
    126 
    127 Each heap uses seggregated free-buckets that have free objects of a specific size. Each free-bucket of a specific size has following 2 lists in it:
    128 \begin{itemize}
    129 \item
    130 Free list is used when a thread is freeing an object that is owned by its own heap so free list does not use any locks/atomic-operations as it is only used by the owner KT.
    131 \item
    132 Away list is used when a thread A is freeing an object that is owned by another KT B's heap. This object should be freed to the owner heap (B's heap) so A will place the object on the away list of B. Away list is lock protected as it is shared by all other threads.
    133 \end{itemize}
    134 
    135 When a dynamic object of a size S is requested. The thread-local heap will check if S is greater than or equal to the mmap threshhold. Any request larger than the mmap threshhold is fulfilled by allocating an mmap area of that size and such requests are not allocated on sbrk area. The value of this threshhold can be changed using mallopt routine but the new value should not be larger than our biggest free-bucket size.
    136 
    137 Algorithm~\ref{alg:heapObjectAlloc} briefly shows how an allocation request is fulfilled.
    138 
    139 \begin{algorithm}
    140 \caption{Dynamic object allocation of size S}\label{alg:heapObjectAlloc}
    141 \begin{algorithmic}[1]
    142 \State $\textit{O} \gets \text{NULL}$
    143 \If {$S < \textit{mmap-threshhold}$}
    144         \State $\textit{B} \gets (\text{smallest free-bucket} \geq S)$
    145         \If {$\textit{B's free-list is empty}$}
    146                 \If {$\textit{B's away-list is empty}$}
    147                         \If {$\textit{heap's allocation buffer} < S$}
    148                                 \State $\text{get allocation buffer using system call sbrk()}$
    149                         \EndIf
    150                         \State $\textit{O} \gets \text{bump allocate an object of size S from allocation buffer}$
    151                 \Else
    152                         \State $\textit{merge B's away-list into free-list}$
    153                         \State $\textit{O} \gets \text{pop an object from B's free-list}$
    154                 \EndIf
    155         \Else
    156                 \State $\textit{O} \gets \text{pop an object from B's free-list}$
    157         \EndIf
    158         \State $\textit{O's owner} \gets \text{B}$
    159 \Else
    160         \State $\textit{O} \gets \text{allocate dynamic memory using system call mmap with size S}$
    161 \EndIf
    162 \State $\Return \textit{ O}$
    163 \end{algorithmic}
    164 \end{algorithm}
    165 
     110FIX ME: Cite performance comparison of the two heap designs if required
    166111
    167112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    168113
    169114\section{Added Features and Methods}
    170 To improve the uHeap allocator (FIX ME: cite uHeap) interface and make it more user friendly, we added a few more routines to the C allocator. Also, we built a \CFA (FIX ME: cite cforall) interface on top of C interface to increase the usability of the allocator.
     115To improve the UHeapLmmm allocator (FIX ME: cite uHeapLmmm) interface and make it more user friendly, we added a few more routines to the C allocator. Also, we built a CFA (FIX ME: cite cforall) interface on top of C interface to increase the usability of the allocator.
    171116
    172117\subsection{C Interface}
    173118We added a few more features and routines to the allocator's C interface that can make the allocator more usable to the programmers. THese features will programmer more control on the dynamic memory allocation.
    174119
    175 \subsection{Out of Memory}
    176 
    177 Most allocators use @nullptr@ to indicate an allocation failure, specifically out of memory;
    178 hence the need to return an alternate value for a zero-sized allocation.
    179 The alternative is to abort a program when out of memory.
    180 In theory, notifying the programmer allows recovery;
    181 in practice, it is almost impossible to gracefully when out of memory, so the cheaper approach of returning @nullptr@ for a zero-sized allocation is chosen.
    182 
    183 
    184 \subsection{\lstinline{void * aalloc( size_t dim, size_t elemSize )}}
    185 @aalloc@ is an extension of malloc. It allows programmer to allocate a dynamic array of objects without calculating the total size of array explicitly. The only alternate of this routine in the other allocators is calloc but calloc also fills the dynamic memory with 0 which makes it slower for a programmer who only wants to dynamically allocate an array of objects without filling it with 0.
    186 \paragraph{Usage}
    187 @aalloc@ takes two parameters.
    188 
    189 \begin{itemize}
    190 \item
    191 @dim@: number of objects in the array
    192 \item
    193 @elemSize@: size of the object in the array.
    194 \end{itemize}
    195 It returns address of dynamic object allocatoed on heap that can contain dim number of objects of the size elemSize. On failure, it returns a @NULL@ pointer.
    196 
    197 \subsection{\lstinline{void * resize( void * oaddr, size_t size )}}
    198 @resize@ is an extension of relloc. It allows programmer to reuse a cuurently allocated dynamic object with a new size requirement. Its alternate in the other allocators is @realloc@ but relloc also copy the data in old object to the new object which makes it slower for the programmer who only wants to reuse an old dynamic object for a new size requirement but does not want to preserve the data in the old object to the new object.
    199 \paragraph{Usage}
    200 @resize@ takes two parameters.
    201 
    202 \begin{itemize}
    203 \item
    204 @oaddr@: the address of the old object that needs to be resized.
    205 \item
    206 @size@: the new size requirement of the to which the old object needs to be resized.
    207 \end{itemize}
    208 It returns an object that is of the size given but it does not preserve the data in the old object. On failure, it returns a @NULL@ pointer.
    209 
    210 \subsection{\lstinline{void * resize( void * oaddr, size_t nalign, size_t size )}}
    211 This @resize@ is an extension of the above @resize@ (FIX ME: cite above resize). In addition to resizing the size of of an old object, it can also realign the old object to a new alignment requirement.
     120\subsubsection void * aalloc( size\_t dim, size\_t elemSize )
     121aalloc is an extension of malloc. It allows programmer to allocate a dynamic array of objects without calculating the total size of array explicitly. The only alternate of this routine in the other allocators is calloc but calloc also fills the dynamic memory with 0 which makes it slower for a programmer who only wants to dynamically allocate an array of objects without filling it with 0.
     122\paragraph{Usage}
     123aalloc takes two parameters.
     124
     125\begin{itemize}
     126\item
     127dim: number of objects in the array
     128\item
     129elemSize: size of the object in the array.
     130\end{itemize}
     131It returns address of dynamic object allocatoed on heap that can contain dim number of objects of the size elemSize. On failure, it returns NULL pointer.
     132
     133\subsubsection void * resize( void * oaddr, size\_t size )
     134resize is an extension of relloc. It allows programmer to reuse a cuurently allocated dynamic object with a new size requirement. Its alternate in the other allocators is realloc but relloc also copy the data in old object to the new object which makes it slower for the programmer who only wants to reuse an old dynamic object for a new size requirement but does not want to preserve the data in the old object to the new object.
     135\paragraph{Usage}
     136resize takes two parameters.
     137
     138\begin{itemize}
     139\item
     140oaddr: the address of the old object that needs to be resized.
     141\item
     142size: the new size requirement of the to which the old object needs to be resized.
     143\end{itemize}
     144It returns an object that is of the size given but it does not preserve the data in the old object. On failure, it returns NULL pointer.
     145
     146\subsubsection void * resize( void * oaddr, size\_t nalign, size\_t size )
     147This resize is an extension of the above resize (FIX ME: cite above resize). In addition to resizing the size of of an old object, it can also realign the old object to a new alignment requirement.
    212148\paragraph{Usage}
    213149This resize takes three parameters. It takes an additional parameter of nalign as compared to the above resize (FIX ME: cite above resize).
     
    215151\begin{itemize}
    216152\item
    217 @oaddr@: the address of the old object that needs to be resized.
    218 \item
    219 @nalign@: the new alignment to which the old object needs to be realigned.
    220 \item
    221 @size@: the new size requirement of the to which the old object needs to be resized.
    222 \end{itemize}
    223 It returns an object with the size and alignment given in the parameters. On failure, it returns a @NULL@ pointer.
    224 
    225 \subsection{\lstinline{void * amemalign( size_t alignment, size_t dim, size_t elemSize )}}
     153oaddr: the address of the old object that needs to be resized.
     154\item
     155nalign: the new alignment to which the old object needs to be realigned.
     156\item
     157size: the new size requirement of the to which the old object needs to be resized.
     158\end{itemize}
     159It returns an object with the size and alignment given in the parameters. On failure, it returns a NULL pointer.
     160
     161\subsubsection void * amemalign( size\_t alignment, size\_t dim, size\_t elemSize )
    226162amemalign is a hybrid of memalign and aalloc. It allows programmer to allocate an aligned dynamic array of objects without calculating the total size of the array explicitly. It frees the programmer from calculating the total size of the array.
    227163\paragraph{Usage}
     
    230166\begin{itemize}
    231167\item
    232 @alignment@: the alignment to which the dynamic array needs to be aligned.
    233 \item
    234 @dim@: number of objects in the array
    235 \item
    236 @elemSize@: size of the object in the array.
    237 \end{itemize}
    238 It returns a dynamic array of objects that has the capacity to contain dim number of objects of the size of elemSize. The returned dynamic array is aligned to the given alignment. On failure, it returns a @NULL@ pointer.
    239 
    240 \subsection{\lstinline{void * cmemalign( size_t alignment, size_t dim, size_t elemSize )}}
     168alignment: the alignment to which the dynamic array needs to be aligned.
     169\item
     170dim: number of objects in the array
     171\item
     172elemSize: size of the object in the array.
     173\end{itemize}
     174It returns a dynamic array of objects that has the capacity to contain dim number of objects of the size of elemSize. The returned dynamic array is aligned to the given alignment. On failure, it returns NULL pointer.
     175
     176\subsubsection void * cmemalign( size\_t alignment, size\_t dim, size\_t elemSize )
    241177cmemalign is a hybrid of amemalign and calloc. It allows programmer to allocate an aligned dynamic array of objects that is 0 filled. The current way to do this in other allocators is to allocate an aligned object with memalign and then fill it with 0 explicitly. This routine provides both features of aligning and 0 filling, implicitly.
    242178\paragraph{Usage}
     
    245181\begin{itemize}
    246182\item
    247 @alignment@: the alignment to which the dynamic array needs to be aligned.
    248 \item
    249 @dim@: number of objects in the array
    250 \item
    251 @elemSize@: size of the object in the array.
    252 \end{itemize}
    253 It returns a dynamic array of objects that has the capacity to contain dim number of objects of the size of elemSize. The returned dynamic array is aligned to the given alignment and is 0 filled. On failure, it returns a @NULL@ pointer.
    254 
    255 \subsection{\lstinline{size_t malloc_alignment( void * addr )}}
    256 @malloc_alignment@ returns the alignment of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required alignment.
    257 \paragraph{Usage}
    258 @malloc_alignment@ takes one parameters.
    259 
    260 \begin{itemize}
    261 \item
    262 @addr@: the address of the currently allocated dynamic object.
    263 \end{itemize}
    264 @malloc_alignment@ returns the alignment of the given dynamic object. On failure, it return the value of default alignment of the uHeap allocator.
    265 
    266 \subsection{\lstinline{bool malloc_zero_fill( void * addr )}}
    267 @malloc_zero_fill@ returns whether a currently allocated dynamic object was initially zero filled at the time of allocation. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verifying the zero filled property of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was zero filled at the time of allocation.
    268 \paragraph{Usage}
    269 @malloc_zero_fill@ takes one parameters.
    270 
    271 \begin{itemize}
    272 \item
    273 @addr@: the address of the currently allocated dynamic object.
    274 \end{itemize}
    275 @malloc_zero_fill@ returns true if the dynamic object was initially zero filled and return false otherwise. On failure, it returns false.
    276 
    277 \subsection{\lstinline{size_t malloc_size( void * addr )}}
    278 @malloc_size@ returns the allocation size of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required size. Its current alternate in the other allocators is @malloc_usable_size@. But, @malloc_size@ is different from @malloc_usable_size@ as @malloc_usabe_size@ returns the total data capacity of dynamic object including the extra space at the end of the dynamic object. On the other hand, @malloc_size@ returns the size that was given to the allocator at the allocation of the dynamic object. This size is updated when an object is realloced, resized, or passed through a similar allocator routine.
    279 \paragraph{Usage}
    280 @malloc_size@ takes one parameters.
    281 
    282 \begin{itemize}
    283 \item
    284 @addr@: the address of the currently allocated dynamic object.
    285 \end{itemize}
    286 @malloc_size@ returns the allocation size of the given dynamic object. On failure, it return zero.
    287 
    288 \subsection{\lstinline{void * realloc( void * oaddr, size_t nalign, size_t size )}}
    289 This @realloc@ is an extension of the default @realloc@ (FIX ME: cite default @realloc@). In addition to reallocating an old object and preserving the data in old object, it can also realign the old object to a new alignment requirement.
    290 \paragraph{Usage}
    291 This @realloc@ takes three parameters. It takes an additional parameter of nalign as compared to the default @realloc@.
    292 
    293 \begin{itemize}
    294 \item
    295 @oaddr@: the address of the old object that needs to be reallocated.
    296 \item
    297 @nalign@: the new alignment to which the old object needs to be realigned.
    298 \item
    299 @size@: the new size requirement of the to which the old object needs to be resized.
    300 \end{itemize}
    301 It returns an object with the size and alignment given in the parameters that preserves the data in the old object. On failure, it returns a @NULL@ pointer.
    302 
    303 \subsection{\CFA Malloc Interface}
    304 We added some routines to the malloc interface of \CFA. These routines can only be used in \CFA and not in our standalone uHeap allocator as these routines use some features that are only provided by \CFA and not by C. It makes the allocator even more usable to the programmers.
    305 \CFA provides the liberty to know the returned type of a call to the allocator. So, mainly in these added routines, we removed the object size parameter from the routine as allocator can calculate the size of the object from the returned type.
    306 
    307 \subsection{\lstinline{T * malloc( void )}}
     183alignment: the alignment to which the dynamic array needs to be aligned.
     184\item
     185dim: number of objects in the array
     186\item
     187elemSize: size of the object in the array.
     188\end{itemize}
     189It returns a dynamic array of objects that has the capacity to contain dim number of objects of the size of elemSize. The returned dynamic array is aligned to the given alignment and is 0 filled. On failure, it returns NULL pointer.
     190
     191\subsubsection size\_t malloc\_alignment( void * addr )
     192malloc\_alignment returns the alignment of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required alignment.
     193\paragraph{Usage}
     194malloc\_alignment takes one parameters.
     195
     196\begin{itemize}
     197\item
     198addr: the address of the currently allocated dynamic object.
     199\end{itemize}
     200malloc\_alignment returns the alignment of the given dynamic object. On failure, it return the value of default alignment of the uHeapLmmm allocator.
     201
     202\subsubsection bool malloc\_zero\_fill( void * addr )
     203malloc\_zero\_fill returns whether a currently allocated dynamic object was initially zero filled at the time of allocation. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verifying the zero filled property of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was zero filled at the time of allocation.
     204\paragraph{Usage}
     205malloc\_zero\_fill takes one parameters.
     206
     207\begin{itemize}
     208\item
     209addr: the address of the currently allocated dynamic object.
     210\end{itemize}
     211malloc\_zero\_fill returns true if the dynamic object was initially zero filled and return false otherwise. On failure, it returns false.
     212
     213\subsubsection size\_t malloc\_size( void * addr )
     214malloc\_size returns the allocation size of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required size. Its current alternate in the other allocators is malloc\_usable\_size. But, malloc\_size is different from malloc\_usable\_size as malloc\_usabe\_size returns the total data capacity of dynamic object including the extra space at the end of the dynamic object. On the other hand, malloc\_size returns the size that was given to the allocator at the allocation of the dynamic object. This size is updated when an object is realloced, resized, or passed through a similar allocator routine.
     215\paragraph{Usage}
     216malloc\_size takes one parameters.
     217
     218\begin{itemize}
     219\item
     220addr: the address of the currently allocated dynamic object.
     221\end{itemize}
     222malloc\_size returns the allocation size of the given dynamic object. On failure, it return zero.
     223
     224\subsubsection void * realloc( void * oaddr, size\_t nalign, size\_t size )
     225This realloc is an extension of the default realloc (FIX ME: cite default realloc). In addition to reallocating an old object and preserving the data in old object, it can also realign the old object to a new alignment requirement.
     226\paragraph{Usage}
     227This realloc takes three parameters. It takes an additional parameter of nalign as compared to the default realloc.
     228
     229\begin{itemize}
     230\item
     231oaddr: the address of the old object that needs to be reallocated.
     232\item
     233nalign: the new alignment to which the old object needs to be realigned.
     234\item
     235size: the new size requirement of the to which the old object needs to be resized.
     236\end{itemize}
     237It returns an object with the size and alignment given in the parameters that preserves the data in the old object. On failure, it returns a NULL pointer.
     238
     239\subsection{CFA Malloc Interface}
     240We added some routines to the malloc interface of CFA. These routines can only be used in CFA and not in our standalone uHeapLmmm allocator as these routines use some features that are only provided by CFA and not by C. It makes the allocator even more usable to the programmers.
     241CFA provides the liberty to know the returned type of a call to the allocator. So, mainly in these added routines, we removed the object size parameter from the routine as allocator can calculate the size of the object from the returned type.
     242
     243\subsubsection T * malloc( void )
    308244This malloc is a simplified polymorphic form of defualt malloc (FIX ME: cite malloc). It does not take any parameter as compared to default malloc that takes one parameter.
    309245\paragraph{Usage}
    310246This malloc takes no parameters.
    311 It returns a dynamic object of the size of type @T@. On failure, it returns a @NULL@ pointer.
    312 
    313 \subsection{\lstinline{T * aalloc( size_t dim )}}
     247It returns a dynamic object of the size of type T. On failure, it return NULL pointer.
     248
     249\subsubsection T * aalloc( size\_t dim )
    314250This aalloc is a simplified polymorphic form of above aalloc (FIX ME: cite aalloc). It takes one parameter as compared to the above aalloc that takes two parameters.
    315251\paragraph{Usage}
     
    318254\begin{itemize}
    319255\item
    320 @dim@: required number of objects in the array.
    321 \end{itemize}
    322 It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type @T@. On failure, it returns a @NULL@ pointer.
    323 
    324 \subsection{\lstinline{T * calloc( size_t dim )}}
     256dim: required number of objects in the array.
     257\end{itemize}
     258It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. On failure, it return NULL pointer.
     259
     260\subsubsection T * calloc( size\_t dim )
    325261This calloc is a simplified polymorphic form of defualt calloc (FIX ME: cite calloc). It takes one parameter as compared to the default calloc that takes two parameters.
    326262\paragraph{Usage}
     
    329265\begin{itemize}
    330266\item
    331 @dim@: required number of objects in the array.
    332 \end{itemize}
    333 It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type @T@. On failure, it returns a @NULL@ pointer.
    334 
    335 \subsection{\lstinline{T * resize( T * ptr, size_t size )}}
    336 This resize is a simplified polymorphic form of above resize (FIX ME: cite resize with alignment). It takes two parameters as compared to the above resize that takes three parameters. It frees the programmer from explicitly mentioning the alignment of the allocation as \CFA provides gives allocator the liberty to get the alignment of the returned type.
     267dim: required number of objects in the array.
     268\end{itemize}
     269It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. On failure, it return NULL pointer.
     270
     271\subsubsection T * resize( T * ptr, size\_t size )
     272This resize is a simplified polymorphic form of above resize (FIX ME: cite resize with alignment). It takes two parameters as compared to the above resize that takes three parameters. It frees the programmer from explicitly mentioning the alignment of the allocation as CFA provides gives allocator the liberty to get the alignment of the returned type.
    337273\paragraph{Usage}
    338274This resize takes two parameters.
     
    340276\begin{itemize}
    341277\item
    342 @ptr@: address of the old object.
    343 \item
    344 @size@: the required size of the new object.
    345 \end{itemize}
    346 It returns a dynamic object of the size given in paramters. The returned object is aligned to the alignemtn of type @T@. On failure, it returns a @NULL@ pointer.
    347 
    348 \subsection{\lstinline{T * realloc( T * ptr, size_t size )}}
    349 This @realloc@ is a simplified polymorphic form of defualt @realloc@ (FIX ME: cite @realloc@ with align). It takes two parameters as compared to the above @realloc@ that takes three parameters. It frees the programmer from explicitly mentioning the alignment of the allocation as \CFA provides gives allocator the liberty to get the alignment of the returned type.
    350 \paragraph{Usage}
    351 This @realloc@ takes two parameters.
    352 
    353 \begin{itemize}
    354 \item
    355 @ptr@: address of the old object.
    356 \item
    357 @size@: the required size of the new object.
    358 \end{itemize}
    359 It returns a dynamic object of the size given in paramters that preserves the data in the given object. The returned object is aligned to the alignemtn of type @T@. On failure, it returns a @NULL@ pointer.
    360 
    361 \subsection{\lstinline{T * memalign( size_t align )}}
     278ptr: address of the old object.
     279\item
     280size: the required size of the new object.
     281\end{itemize}
     282It returns a dynamic object of the size given in paramters. The returned object is aligned to the alignemtn of type T. On failure, it return NULL pointer.
     283
     284\subsubsection T * realloc( T * ptr, size\_t size )
     285This realloc is a simplified polymorphic form of defualt realloc (FIX ME: cite realloc with align). It takes two parameters as compared to the above realloc that takes three parameters. It frees the programmer from explicitly mentioning the alignment of the allocation as CFA provides gives allocator the liberty to get the alignment of the returned type.
     286\paragraph{Usage}
     287This realloc takes two parameters.
     288
     289\begin{itemize}
     290\item
     291ptr: address of the old object.
     292\item
     293size: the required size of the new object.
     294\end{itemize}
     295It returns a dynamic object of the size given in paramters that preserves the data in the given object. The returned object is aligned to the alignemtn of type T. On failure, it return NULL pointer.
     296
     297\subsubsection T * memalign( size\_t align )
    362298This memalign is a simplified polymorphic form of defualt memalign (FIX ME: cite memalign). It takes one parameters as compared to the default memalign that takes two parameters.
    363299\paragraph{Usage}
     
    366302\begin{itemize}
    367303\item
    368 @align@: the required alignment of the dynamic object.
    369 \end{itemize}
    370 It returns a dynamic object of the size of type @T@ that is aligned to given parameter align. On failure, it returns a @NULL@ pointer.
    371 
    372 \subsection{\lstinline{T * amemalign( size_t align, size_t dim )}}
     304align: the required alignment of the dynamic object.
     305\end{itemize}
     306It returns a dynamic object of the size of type T that is aligned to given parameter align. On failure, it return NULL pointer.
     307
     308\subsubsection T * amemalign( size\_t align, size\_t dim )
    373309This amemalign is a simplified polymorphic form of above amemalign (FIX ME: cite amemalign). It takes two parameter as compared to the above amemalign that takes three parameters.
    374310\paragraph{Usage}
     
    377313\begin{itemize}
    378314\item
    379 @align@: required alignment of the dynamic array.
    380 \item
    381 @dim@: required number of objects in the array.
    382 \end{itemize}
    383 It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type @T@. The returned object is aligned to the given parameter align. On failure, it returns a @NULL@ pointer.
    384 
    385 \subsection{\lstinline{T * cmemalign( size_t align, size_t dim  )}}
     315align: required alignment of the dynamic array.
     316\item
     317dim: required number of objects in the array.
     318\end{itemize}
     319It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. The returned object is aligned to the given parameter align. On failure, it return NULL pointer.
     320
     321\subsubsection T * cmemalign( size\_t align, size\_t dim  )
    386322This cmemalign is a simplified polymorphic form of above cmemalign (FIX ME: cite cmemalign). It takes two parameter as compared to the above cmemalign that takes three parameters.
    387323\paragraph{Usage}
     
    390326\begin{itemize}
    391327\item
    392 @align@: required alignment of the dynamic array.
    393 \item
    394 @dim@: required number of objects in the array.
    395 \end{itemize}
    396 It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type @T@. The returned object is aligned to the given parameter align and is zero filled. On failure, it returns a @NULL@ pointer.
    397 
    398 \subsection{\lstinline{T * aligned_alloc( size_t align )}}
    399 This @aligned_alloc@ is a simplified polymorphic form of defualt @aligned_alloc@ (FIX ME: cite @aligned_alloc@). It takes one parameter as compared to the default @aligned_alloc@ that takes two parameters.
    400 \paragraph{Usage}
    401 This @aligned_alloc@ takes one parameter.
    402 
    403 \begin{itemize}
    404 \item
    405 @align@: required alignment of the dynamic object.
    406 \end{itemize}
    407 It returns a dynamic object of the size of type @T@ that is aligned to the given parameter. On failure, it returns a @NULL@ pointer.
    408 
    409 \subsection{\lstinline{int posix_memalign( T ** ptr, size_t align )}}
    410 This @posix_memalign@ is a simplified polymorphic form of defualt @posix_memalign@ (FIX ME: cite @posix_memalign@). It takes two parameters as compared to the default @posix_memalign@ that takes three parameters.
    411 \paragraph{Usage}
    412 This @posix_memalign@ takes two parameter.
    413 
    414 \begin{itemize}
    415 \item
    416 @ptr@: variable address to store the address of the allocated object.
    417 \item
    418 @align@: required alignment of the dynamic object.
    419 \end{itemize}
    420 
    421 It stores address of the dynamic object of the size of type @T@ in given parameter ptr. This object is aligned to the given parameter. On failure, it returns a @NULL@ pointer.
    422 
    423 \subsection{\lstinline{T * valloc( void )}}
    424 This @valloc@ is a simplified polymorphic form of defualt @valloc@ (FIX ME: cite @valloc@). It takes no parameters as compared to the default @valloc@ that takes one parameter.
    425 \paragraph{Usage}
    426 @valloc@ takes no parameters.
    427 It returns a dynamic object of the size of type @T@ that is aligned to the page size. On failure, it returns a @NULL@ pointer.
    428 
    429 \subsection{\lstinline{T * pvalloc( void )}}
    430 \paragraph{Usage}
    431 @pvalloc@ takes no parameters.
    432 It returns a dynamic object of the size that is calcutaed by rouding the size of type @T@. The returned object is also aligned to the page size. On failure, it returns a @NULL@ pointer.
    433 
    434 \subsection{Alloc Interface}
    435 In addition to improve allocator interface both for \CFA and our standalone allocator uHeap in C. We also added a new alloc interface in \CFA that increases usability of dynamic memory allocation.
     328align: required alignment of the dynamic array.
     329\item
     330dim: required number of objects in the array.
     331\end{itemize}
     332It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. The returned object is aligned to the given parameter align and is zero filled. On failure, it return NULL pointer.
     333
     334\subsubsection T * aligned\_alloc( size\_t align )
     335This aligned\_alloc is a simplified polymorphic form of defualt aligned\_alloc (FIX ME: cite aligned\_alloc). It takes one parameter as compared to the default aligned\_alloc that takes two parameters.
     336\paragraph{Usage}
     337This aligned\_alloc takes one parameter.
     338
     339\begin{itemize}
     340\item
     341align: required alignment of the dynamic object.
     342\end{itemize}
     343It returns a dynamic object of the size of type T that is aligned to the given parameter. On failure, it return NULL pointer.
     344
     345\subsubsection int posix\_memalign( T ** ptr, size\_t align )
     346This posix\_memalign is a simplified polymorphic form of defualt posix\_memalign (FIX ME: cite posix\_memalign). It takes two parameters as compared to the default posix\_memalign that takes three parameters.
     347\paragraph{Usage}
     348This posix\_memalign takes two parameter.
     349
     350\begin{itemize}
     351\item
     352ptr: variable address to store the address of the allocated object.
     353\item
     354align: required alignment of the dynamic object.
     355\end{itemize}
     356
     357It stores address of the dynamic object of the size of type T in given parameter ptr. This object is aligned to the given parameter. On failure, it return NULL pointer.
     358
     359\subsubsection T * valloc( void )
     360This valloc is a simplified polymorphic form of defualt valloc (FIX ME: cite valloc). It takes no parameters as compared to the default valloc that takes one parameter.
     361\paragraph{Usage}
     362valloc takes no parameters.
     363It returns a dynamic object of the size of type T that is aligned to the page size. On failure, it return NULL pointer.
     364
     365\subsubsection T * pvalloc( void )
     366This pcvalloc is a simplified polymorphic form of defualt pcvalloc (FIX ME: cite pcvalloc). It takes no parameters as compared to the default pcvalloc that takes one parameter.
     367\paragraph{Usage}
     368pvalloc takes no parameters.
     369It returns a dynamic object of the size that is calcutaed by rouding the size of type T. The returned object is also aligned to the page size. On failure, it return NULL pointer.
     370
     371\subsection Alloc Interface
     372In addition to improve allocator interface both for CFA and our standalone allocator uHeapLmmm in C. We also added a new alloc interface in CFA that increases usability of dynamic memory allocation.
    436373This interface helps programmers in three major ways.
    437374
     
    442379Parametre Positions: alloc interface frees programmers from remembering parameter postions in call to routines.
    443380\item
    444 Object Size: alloc interface does not require programmer to mention the object size as \CFA allows allocator to determince the object size from returned type of alloc call.
    445 \end{itemize}
    446 
    447 Alloc interface uses polymorphism, backtick routines (FIX ME: cite backtick) and ttype parameters of \CFA (FIX ME: cite ttype) to provide a very simple dynamic memory allocation interface to the programmers. The new interfece has just one routine name alloc that can be used to perform a wide range of dynamic allocations. The parameters use backtick functions to provide a similar-to named parameters feature for our alloc interface so that programmers do not have to remember parameter positions in alloc call except the position of dimension (dim) parameter.
    448 
    449 \subsection{Routine: \lstinline{T * alloc( ... )}}
    450 Call to alloc wihout any parameter returns one object of size of type @T@ allocated dynamically.
     381Object Size: alloc interface does not require programmer to mention the object size as CFA allows allocator to determince the object size from returned type of alloc call.
     382\end{itemize}
     383
     384Alloc interface uses polymorphism, backtick routines (FIX ME: cite backtick) and ttype parameters of CFA (FIX ME: cite ttype) to provide a very simple dynamic memory allocation interface to the programmers. The new interfece has just one routine name alloc that can be used to perform a wide range of dynamic allocations. The parameters use backtick functions to provide a similar-to named parameters feature for our alloc interface so that programmers do not have to remember parameter positions in alloc call except the position of dimension (dim) parameter.
     385
     386\subsubsection{Routine: T * alloc( ... )}
     387Call to alloc wihout any parameter returns one object of size of type T allocated dynamically.
    451388Only the dimension (dim) parameter for array allocation has the fixed position in the alloc routine. If programmer wants to allocate an array of objects that the required number of members in the array has to be given as the first parameter to the alloc routine.
    452 alocc routine accepts six kinds of arguments. Using different combinations of tha parameters, different kind of allocations can be performed. Any combincation of parameters can be used together except @`realloc@ and @`resize@ that should not be used simultanously in one call to routine as it creates ambiguity about whether to reallocate or resize a currently allocated dynamic object. If both @`resize@ and @`realloc@ are used in a call to alloc then the latter one will take effect or unexpected resulted might be produced.
     389alocc routine accepts six kinds of arguments. Using different combinations of tha parameters, different kind of allocations can be performed. Any combincation of parameters can be used together except `realloc and `resize that should not be used simultanously in one call to routine as it creates ambiguity about whether to reallocate or resize a currently allocated dynamic object. If both `resize and `realloc are used in a call to alloc then the latter one will take effect or unexpected resulted might be produced.
    453390
    454391\paragraph{Dim}
    455 This is the only parameter in the alloc routine that has a fixed-position and it is also the only parameter that does not use a backtick function. It has to be passed at the first position to alloc call in-case of an array allocation of objects of type @T@.
    456 It represents the required number of members in the array allocation as in \CFA's aalloc (FIX ME: cite aalloc).
    457 This parameter should be of type @size_t@.
    458 
    459 Example: @int a = alloc( 5 )@
     392This is the only parameter in the alloc routine that has a fixed-position and it is also the only parameter that does not use a backtick function. It has to be passed at the first position to alloc call in-case of an array allocation of objects of type T.
     393It represents the required number of members in the array allocation as in CFA's aalloc (FIX ME: cite aalloc).
     394This parameter should be of type size\_t.
     395
     396Example: int a = alloc( 5 )
    460397This call will return a dynamic array of five integers.
    461398
    462399\paragraph{Align}
    463 This parameter is position-free and uses a backtick routine align (@`align@). The parameter passed with @`align@ should be of type @size_t@. If the alignment parameter is not a power of two or is less than the default alignment of the allocator (that can be found out using routine libAlign in \CFA) then the passed alignment parameter will be rejected and the default alignment will be used.
    464 
    465 Example: @int b = alloc( 5 , 64`align )@
     400This parameter is position-free and uses a backtick routine align (`align). The parameter passed with `align should be of type size\_t. If the alignment parameter is not a power of two or is less than the default alignment of the allocator (that can be found out using routine libAlign in CFA) then the passed alignment parameter will be rejected and the default alignment will be used.
     401
     402Example: int b = alloc( 5 , 64`align )
    466403This call will return a dynamic array of five integers. It will align the allocated object to 64.
    467404
    468405\paragraph{Fill}
    469 This parameter is position-free and uses a backtick routine fill (@`fill@). In case of @realloc@, only the extra space after copying the data in the old object will be filled with given parameter.
     406This parameter is position-free and uses a backtick routine fill (`fill). In case of realloc, only the extra space after copying the data in the old object will be filled with given parameter.
    470407Three types of parameters can be passed using `fill.
    471408
    472409\begin{itemize}
    473410\item
    474 @char@: A char can be passed with @`fill@ to fill the whole dynamic allocation with the given char recursively till the end of required allocation.
    475 \item
    476 Object of returned type: An object of type of returned type can be passed with @`fill@ to fill the whole dynamic allocation with the given object recursively till the end of required allocation.
    477 \item
    478 Dynamic object of returned type: A dynamic object of type of returned type can be passed with @`fill@ to fill the dynamic allocation with the given dynamic object. In this case, the allocated memory is not filled recursively till the end of allocation. The filling happen untill the end object passed to @`fill@ or the end of requested allocation reaches.
    479 \end{itemize}
    480 
    481 Example: @int b = alloc( 5 , 'a'`fill )@
     411char: A char can be passed with `fill to fill the whole dynamic allocation with the given char recursively till the end of required allocation.
     412\item
     413Object of returned type: An object of type of returned type can be passed with `fill to fill the whole dynamic allocation with the given object recursively till the end of required allocation.
     414\item
     415Dynamic object of returned type: A dynamic object of type of returned type can be passed with `fill to fill the dynamic allocation with the given dynamic object. In this case, the allocated memory is not filled recursively till the end of allocation. The filling happen untill the end object passed to `fill or the end of requested allocation reaches.
     416\end{itemize}
     417
     418Example: int b = alloc( 5 , 'a'`fill )
    482419This call will return a dynamic array of five integers. It will fill the allocated object with character 'a' recursively till the end of requested allocation size.
    483420
    484 Example: @int b = alloc( 5 , 4`fill )@
     421Example: int b = alloc( 5 , 4`fill )
    485422This call will return a dynamic array of five integers. It will fill the allocated object with integer 4 recursively till the end of requested allocation size.
    486423
    487 Example: @int b = alloc( 5 , a`fill )@ where @a@ is a pointer of int type
     424Example: int b = alloc( 5 , a`fill ) where a is a pointer of int type
    488425This call will return a dynamic array of five integers. It will copy data in a to the returned object non-recursively untill end of a or the newly allocated object is reached.
    489426
    490427\paragraph{Resize}
    491 This parameter is position-free and uses a backtick routine resize (@`resize@). It represents the old dynamic object (oaddr) that the programmer wants to
     428This parameter is position-free and uses a backtick routine resize (`resize). It represents the old dynamic object (oaddr) that the programmer wants to
    492429\begin{itemize}
    493430\item
     
    498435fill with something.
    499436\end{itemize}
    500 The data in old dynamic object will not be preserved in the new object. The type of object passed to @`resize@ and the returned type of alloc call can be different.
    501 
    502 Example: @int b = alloc( 5 , a`resize )@
     437The data in old dynamic object will not be preserved in the new object. The type of object passed to `resize and the returned type of alloc call can be different.
     438
     439Example: int b = alloc( 5 , a`resize )
    503440This call will resize object a to a dynamic array that can contain 5 integers.
    504441
    505 Example: @int b = alloc( 5 , a`resize , 32`align )@
     442Example: int b = alloc( 5 , a`resize , 32`align )
    506443This call will resize object a to a dynamic array that can contain 5 integers. The returned object will also be aligned to 32.
    507444
    508 Example: @int b = alloc( 5 , a`resize , 32`align , 2`fill )@
     445Example: int b = alloc( 5 , a`resize , 32`align , 2`fill)
    509446This call will resize object a to a dynamic array that can contain 5 integers. The returned object will also be aligned to 32 and will be filled with 2.
    510447
    511448\paragraph{Realloc}
    512 This parameter is position-free and uses a backtick routine @realloc@ (@`realloc@). It represents the old dynamic object (oaddr) that the programmer wants to
     449This parameter is position-free and uses a backtick routine realloc (`realloc). It represents the old dynamic object (oaddr) that the programmer wants to
    513450\begin{itemize}
    514451\item
     
    519456fill with something.
    520457\end{itemize}
    521 The data in old dynamic object will be preserved in the new object. The type of object passed to @`realloc@ and the returned type of alloc call cannot be different.
    522 
    523 Example: @int b = alloc( 5 , a`realloc )@
     458The data in old dynamic object will be preserved in the new object. The type of object passed to `realloc and the returned type of alloc call cannot be different.
     459
     460Example: int b = alloc( 5 , a`realloc )
    524461This call will realloc object a to a dynamic array that can contain 5 integers.
    525462
    526 Example: @int b = alloc( 5 , a`realloc , 32`align )@
     463Example: int b = alloc( 5 , a`realloc , 32`align )
    527464This call will realloc object a to a dynamic array that can contain 5 integers. The returned object will also be aligned to 32.
    528465
    529 Example: @int b = alloc( 5 , a`realloc , 32`align , 2`fill )@
     466Example: int b = alloc( 5 , a`realloc , 32`align , 2`fill)
    530467This call will resize object a to a dynamic array that can contain 5 integers. The returned object will also be aligned to 32. The extra space after copying data of a to the returned object will be filled with 2.
  • doc/theses/mubeen_zulfiqar_MMath/background.tex

    r92538ab r4559b34  
    1 \begin{comment}
     1\chapter{Background}
     2
     3\noindent
    24====================
     5
    36Writing Points:
    47\begin{itemize}
     
    1619Features and limitations.
    1720\end{itemize}
    18 \end{comment}
    1921
    20 \chapter[Background]{Background\footnote{Part of this chapter draws from similar background work in~\cite{wasik.thesis} with many updates.}}
     22\noindent
     23====================
    2124
     25\section{Background}
    2226
    23 A program dynamically allocates and deallocates the storage for a variable, referred to as an \newterm{object}, through calls such as @malloc@ and @free@ in C, and @new@ and @delete@ in \CC.
    24 Space for each allocated object comes from the dynamic-allocation zone.
    25 A \newterm{memory allocator} contains a complex data-structure and code that manages the layout of objects in the dynamic-allocation zone.
    26 The management goals are to make allocation/deallocation operations as fast as possible while densely packing objects to make efficient use of memory.
    27 Objects in C/\CC cannot be moved to aid the packing process, only adjacent free storage can be \newterm{coalesced} into larger free areas.
    28 The allocator grows or shrinks the dynamic-allocation zone to obtain storage for objects and reduce memory usage via operating-system calls, such as @mmap@ or @sbrk@ in UNIX.
     27% FIXME: cite wasik
     28\cite{wasik.thesis}
    2929
     30\subsection{Memory Allocation}
     31With dynamic allocation being an important feature of C, there are many standalone memory allocators that have been designed for different purposes. For this thesis, we chose 7 of the most popular and widely used memory allocators.
    3032
    31 \section{Allocator Components}
    32 \label{s:AllocatorComponents}
     33\paragraph{dlmalloc}
     34dlmalloc (FIX ME: cite allocator) is a thread-safe allocator that is single threaded and single heap. dlmalloc maintains free-lists of different sizes to store freed dynamic memory. (FIX ME: cite wasik)
    3335
    34 \VRef[Figure]{f:AllocatorComponents} shows the two important data components for a memory allocator, management and storage, collectively called the \newterm{heap}.
    35 The \newterm{management data} is a data structure located at a known memory address and contains all information necessary to manage the storage data.
    36 The management data starts with fixed-sized information in the static-data memory that references components in the dynamic-allocation memory.
    37 The \newterm{storage data} is composed of allocated and freed objects, and \newterm{reserved memory}.
    38 Allocated objects (white) are variable sized, and allocated and maintained by the program;
    39 \ie only the program knows the location of allocated storage, not the memory allocator.
    40 \begin{figure}[h]
    41 \centering
    42 \input{AllocatorComponents}
    43 \caption{Allocator Components (Heap)}
    44 \label{f:AllocatorComponents}
    45 \end{figure}
    46 Freed objects (light grey) represent memory deallocated by the program, which are linked into one or more lists facilitating easy location of new allocations.
    47 Often the free list is chained internally so it does not consume additional storage, \ie the link fields are placed at known locations in the unused memory blocks.
    48 Reserved memory (dark grey) is one or more blocks of memory obtained from the operating system but not yet allocated to the program;
    49 if there are multiple reserved blocks, they are also chained together, usually internally.
     36\paragraph{hoard}
     37Hoard (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and using a heap layer framework. It has per-thred heaps that have thread-local free-lists, and a gloabl shared heap. (FIX ME: cite wasik)
    5038
    51 Allocated and freed objects typically have additional management data embedded within them.
    52 \VRef[Figure]{f:AllocatedObject} shows an allocated object with a header, trailer, and alignment padding and spacing around the object.
    53 The header contains information about the object, \eg size, type, etc.
    54 The trailer may be used to simplify an allocation implementation, \eg coalescing, and/or for security purposes to mark the end of an object.
    55 An object may be preceded by padding to ensure proper alignment.
    56 Some algorithms quantize allocation requests into distinct sizes resulting in additional spacing after objects less than the quantized value.
    57 When padding and spacing are necessary, neither can be used to satisfy a future allocation request while the current allocation exists.
    58 A free object also contains management data, \eg size, chaining, etc.
    59 The amount of management data for a free node defines the minimum allocation size, \eg if 16 bytes are needed for a free-list node, any allocation request less than 16 bytes must be rounded up, otherwise the free list cannot use internal chaining.
    60 The information in an allocated or freed object is overwritten when it transitions from allocated to freed and vice-versa by new management information and possibly data.
     39\paragraph{jemalloc}
     40jemalloc (FIX ME: cite allocator) is a thread-safe allocator that uses multiple arenas. Each thread is assigned an arena. Each arena has chunks that contain contagious memory regions of same size. An arena has multiple chunks that contain regions of multiple sizes.
    6141
    62 \begin{figure}
    63 \centering
    64 \input{AllocatedObject}
    65 \caption{Allocated Object}
    66 \label{f:AllocatedObject}
    67 \end{figure}
     42\paragraph{ptmalloc}
     43ptmalloc (FIX ME: cite allocator) is a modification of dlmalloc. It is a thread-safe multi-threaded memory allocator that uses multiple heaps. ptmalloc heap has similar design to dlmalloc's heap.
    6844
     45\paragraph{rpmalloc}
     46rpmalloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses per-thread heap. Each heap has multiple size-classes and each size-calss contains memory regions of the relevant size.
    6947
    70 \section{Single-Threaded Memory-Allocator}
    71 \label{s:SingleThreadedMemoryAllocator}
     48\paragraph{tbb malloc}
     49tbb malloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses private heap for each thread. Each private-heap has multiple bins of different sizes. Each bin contains free regions of the same size.
    7250
    73 A single-threaded memory-allocator does not run any threads itself, but is used by a single-threaded program.
    74 Because the memory allocator is only executed by a single thread, concurrency issues do not exist.
    75 The primary issues in designing a single-threaded memory-allocator are fragmentation and locality.
     51\paragraph{tc malloc}
     52tcmalloc (FIX ME: cite allocator) is a thread-safe allocator. It uses per-thread cache to store free objects that prevents contention on shared resources in multi-threaded application. A central free-list is used to refill per-thread cache when it gets empty.
    7653
     54\subsection{Benchmarks}
     55There are multiple benchmarks that are built individually and evaluate different aspects of a memory allocator. But, there is not standard set of benchamrks that can be used to evaluate multiple aspects of memory allocators.
    7756
    78 \subsection{Fragmentation}
    79 \label{s:Fragmentation}
     57\paragraph{threadtest}
     58(FIX ME: cite benchmark and hoard) Each thread repeatedly allocates and then deallocates 100,000 objects. Runtime of the benchmark evaluates its efficiency.
    8059
    81 Fragmentation is memory requested from the operating system but not used by the program;
    82 hence, allocated objects are not fragmentation.
    83 \VRef[Figure]{f:InternalExternalFragmentation} shows fragmentation is divided into two forms: internal or external.
     60\paragraph{shbench}
     61(FIX ME: cite benchmark and hoard) Each thread allocates and randomly frees a number of random-sized objects. It is a stress test that also uses runtime to determine efficiency of the allocator.
    8462
    85 \begin{figure}
    86 \centering
    87 \input{IntExtFragmentation}
    88 \caption{Internal and External Fragmentation}
    89 \label{f:InternalExternalFragmentation}
    90 \end{figure}
    91 
    92 \newterm{Internal fragmentation} is memory space that is allocated to the program, but is not intended to be accessed by the program, such as headers, trailers, padding, and spacing around an allocated object.
    93 This memory is typically used by the allocator for management purposes or required by the architecture for correctness, \eg alignment.
    94 Internal fragmentation is problematic when management space is a significant proportion of an allocated object.
    95 For example, if internal fragmentation is as large as the object being managed, then the memory usage for that object is doubled.
    96 An allocator should strive to keep internal management information to a minimum.
    97 
    98 \newterm{External fragmentation} is all memory space reserved from the operating system but not allocated to the program~\cite{Wilson95,Lim98,Siebert00}, which includes all external management data, freed objects, and reserved memory.
    99 This memory is problematic in two ways: heap blowup and highly fragmented memory.
    100 \newterm{Heap blowup} occurs when memory freed by the program is not reused for future allocations leading to potentially unbounded external fragmentation growth~\cite{Berger00}.
    101 Heap blowup can occur due to allocator policies that are too restrictive in reusing freed memory and/or no coalescing of free storage.
    102 Memory can become \newterm{highly fragmented} after multiple allocations and deallocations of objects.
    103 \VRef[Figure]{f:MemoryFragmentation} shows an example of how a small block of memory fragments as objects are allocated and deallocated over time.
    104 Blocks of free memory become smaller and non-contiguous making them less useful in serving allocation requests.
    105 Memory is highly fragmented when the sizes of most free blocks are unusable.
    106 For example, \VRef[Figure]{f:Contiguous} and \VRef[Figure]{f:HighlyFragmented} have the same quantity of external fragmentation, but \VRef[Figure]{f:HighlyFragmented} is highly fragmented.
    107 If there is a request to allocate a large object, \VRef[Figure]{f:Contiguous} is more likely to be able to satisfy it with existing free memory, while \VRef[Figure]{f:HighlyFragmented} likely has to request more memory from the operating system.
    108 
    109 \begin{figure}
    110 \centering
    111 \input{MemoryFragmentation}
    112 \caption{Memory Fragmentation}
    113 \label{f:MemoryFragmentation}
    114 \vspace{10pt}
    115 \subfigure[Contiguous]{
    116         \input{ContigFragmentation}
    117         \label{f:Contiguous}
    118 } % subfigure
    119         \subfigure[Highly Fragmented]{
    120         \input{NonContigFragmentation}
    121 \label{f:HighlyFragmented}
    122 } % subfigure
    123 \caption{Fragmentation Quality}
    124 \label{f:FragmentationQuality}
    125 \end{figure}
    126 
    127 For a single-threaded memory allocator, three basic approaches for controlling fragmentation are identified~\cite{Johnstone99}.
    128 The first approach is a \newterm{sequential-fit algorithm} with one list of free objects that is searched for a block large enough to fit a requested object size.
    129 Different search policies determine the free object selected, \eg the first free object large enough or closest to the requested size.
    130 Any storage larger than the request can become spacing after the object or be split into a smaller free object.
    131 The cost of the search depends on the shape and quality of the free list, \eg a linear versus a binary-tree free-list, a sorted versus unsorted free-list.
    132 
    133 The second approach is a \newterm{segregated} or \newterm{binning algorithm} with a set of lists for different sized freed objects.
    134 When an object is allocated, the requested size is rounded up to the nearest bin-size, often leading to spacing after the object.
    135 A binning algorithm is fast at finding free memory of the appropriate size and allocating it, since the first free object on the free list is used.
    136 The fewer bin-sizes, the fewer lists need to be searched and maintained;
    137 however, the bin sizes are less likely to closely fit the requested object size, leading to more internal fragmentation.
    138 The more bin-sizes, the longer the search and the less likely free objects are to be reused, leading to more external fragmentation and potentially heap blowup.
    139 A variation of the binning algorithm allows objects to be allocated to the requested size, but when an object is freed, it is placed on the free list of the next smallest or equal bin-size.
    140 For example, with bin sizes of 8 and 16 bytes, a request for 12 bytes allocates only 12 bytes, but when the object is freed, it is placed on the 8-byte bin-list.
    141 For subsequent requests, the bin free-lists contain objects of different sizes, ranging from one bin-size to the next (8-16 in this example), and a sequential-fit algorithm may be used to find an object large enough for the requested size on the associated bin list.
    142 
    143 The third approach is \newterm{splitting} and \newterm{coalescing algorithms}.
    144 When an object is allocated, if there are no free objects of the requested size, a larger free object may be split into two smaller objects to satisfy the allocation request without obtaining more memory from the operating system.
    145 For example, in the buddy system, a block of free memory is split into two equal chunks, one of those chunks is again split into two equal chunks, and so on until a block just large enough to fit the requested object is created.
    146 When an object is deallocated it is coalesced with the objects immediately before and after it in memory, if they are free, turning them into one larger object.
    147 Coalescing can be done eagerly at each deallocation or lazily when an allocation cannot be fulfilled.
    148 In all cases, coalescing increases allocation latency, hence some allocations can cause unbounded delays during coalescing.
    149 While coalescing does not reduce external fragmentation, the coalesced blocks improve fragmentation quality so future allocations are less likely to cause heap blowup.
    150 Splitting and coalescing can be used with other algorithms to avoid highly fragmented memory.
    151 
    152 
    153 \subsection{Locality}
    154 \label{s:Locality}
    155 
    156 The principle of locality recognizes that programs tend to reference a small set of data, called a working set, for a certain period of time, where a working set is composed of temporal and spatial accesses~\cite{Denning05}.
    157 Temporal clustering implies a group of objects are accessed repeatedly within a short time period, while spatial clustering implies a group of objects physically close together (nearby addresses) are accessed repeatedly within a short time period.
    158 Temporal locality commonly occurs during an iterative computation with a fix set of disjoint variables, while spatial locality commonly occurs when traversing an array.
    159 
    160 Hardware takes advantage of temporal and spatial locality through multiple levels of caching, \ie memory hierarchy.
    161 When an object is accessed, the memory physically located around the object is also cached with the expectation that the current and nearby objects will be referenced within a short period of time.
    162 For example, entire cache lines are transferred between memory and cache and entire virtual-memory pages are transferred between disk and memory.
    163 A program exhibiting good locality has better performance due to fewer cache misses and page faults\footnote{With the advent of large RAM memory, paging is becoming less of an issue in modern programming.}.
    164 
    165 Temporal locality is largely controlled by how a program accesses its variables~\cite{Feng05}.
    166 Nevertheless, a memory allocator can have some indirect influence on temporal locality and largely dictates spatial locality.
    167 For temporal locality, an allocator can return storage for new allocations that was just freed as these memory locations are still \emph{warm} in the memory hierarchy.
    168 For spatial locality, an allocator can place objects used together close together in memory, so the working set of the program fits into the fewest possible cache lines and pages.
    169 However, usage patterns are different for every program as is the underlying hardware memory architecture;
    170 hence, no general-purpose memory-allocator can provide ideal locality for every program on every computer.
    171 
    172 There are a number of ways a memory allocator can degrade locality by increasing the working set.
    173 For example, a memory allocator may access multiple free objects before finding one to satisfy an allocation request, \eg sequential-fit algorithm.
    174 If there are a (large) number of objects accessed in very different areas of memory, the allocator may perturb the program's memory hierarchy causing multiple cache or page misses~\cite{Grunwald93}.
    175 Another way locality can be degraded is by spatially separating related data.
    176 For example, in a binning allocator, objects of different sizes are allocated from different bins that may be located in different pages of memory.
    177 
    178 
    179 \section{Multi-Threaded Memory-Allocator}
    180 \label{s:MultiThreadedMemoryAllocator}
    181 
    182 A multi-threaded memory-allocator does not run any threads itself, but is used by a multi-threaded program.
    183 In addition to single-threaded design issues of fragmentation and locality, a multi-threaded allocator is simultaneously accessed by multiple threads, and hence, must deal with concurrency issues such as mutual exclusion, false sharing, and additional forms of heap blowup.
    184 
    185 
    186 \subsection{Mutual Exclusion}
    187 \label{s:MutualExclusion}
    188 
    189 \newterm{Mutual exclusion} provides sequential access to the shared management data of the heap.
    190 There are two performance issues for mutual exclusion.
    191 First is the overhead necessary to perform (at least) a hardware atomic operation every time a shared resource is accessed.
    192 Second is when multiple threads contend for a shared resource simultaneously, and hence, some threads must wait until the resource is released.
    193 Contention can be reduced in a number of ways:
    194 \begin{itemize}[itemsep=0pt]
    195 \item
    196 using multiple fine-grained locks versus a single lock, spreading the contention across a number of locks;
    197 \item
    198 using trylock and generating new storage if the lock is busy, yielding a classic space versus time tradeoff;
    199 \item
    200 using one of the many lock-free approaches for reducing contention on basic data-structure operations~\cite{Oyama99}.
    201 \end{itemize}
    202 However, all of these approaches have degenerate cases where program contention is high, which occurs outside of the allocator.
    203 
    204 
    205 \subsection{False Sharing}
    206 \label{s:FalseSharing}
    207 
    208 False sharing is a dynamic phenomenon leading to cache thrashing.
    209 When two or more threads on separate CPUs simultaneously change different objects sharing a cache line, the change invalidates the other thread's associated cache, even though these threads may be uninterested in the other modified object.
    210 False sharing can occur in three different ways: program induced, allocator-induced active, and allocator-induced passive;
    211 a memory allocator can only affect the latter two.
    212 
    213 \paragraph{\newterm{Program-induced false-sharing}} occurs when one thread passes an object sharing a cache line to another thread, and both threads modify the respective objects.
    214 \VRef[Figure]{f:ProgramInducedFalseSharing} shows when Task$_1$ passes Object$_2$ to Task$_2$, a false-sharing situation forms when Task$_1$ modifies Object$_1$ and Task$_2$ modifies Object$_2$.
    215 Changes to Object$_1$ invalidate CPU$_2$'s cache line, and changes to Object$_2$ invalidate CPU$_1$'s cache line.
    216 
    217 \begin{figure}
    218 \centering
    219 \subfigure[Program-Induced False-Sharing]{
    220         \input{ProgramFalseSharing}
    221         \label{f:ProgramInducedFalseSharing}
    222 } \\
    223 \vspace{5pt}
    224 \subfigure[Allocator-Induced Active False-Sharing]{
    225         \input{AllocInducedActiveFalseSharing}
    226         \label{f:AllocatorInducedActiveFalseSharing}
    227 } \\
    228 \vspace{5pt}
    229 \subfigure[Allocator-Induced Passive False-Sharing]{
    230         \input{AllocInducedPassiveFalseSharing}
    231         \label{f:AllocatorInducedPassiveFalseSharing}
    232 } % subfigure
    233 \caption{False Sharing}
    234 \label{f:FalseSharing}
    235 \end{figure}
    236 
    237 \paragraph{\newterm{Allocator-induced active false-sharing}} occurs when objects are allocated within the same cache line but to different threads.
    238 For example, in \VRef[Figure]{f:AllocatorInducedActiveFalseSharing}, each task allocates an object and loads a cache-line of memory into its associated cache.
    239 Again, changes to Object$_1$ invalidate CPU$_2$'s cache line, and changes to Object$_2$ invalidate CPU$_1$'s cache line.
    240 
    241 \paragraph{\newterm{Allocator-induced passive false-sharing}} is another form of allocator-induced false-sharing caused by program-induced false-sharing.
    242 When an object in a program-induced false-sharing situation is deallocated, a future allocation of that object may cause passive false-sharing.
    243 For example, in \VRef[Figure]{f:AllocatorInducedPassiveFalseSharing}, Task$_1$ passes Object$_2$ to Task$_2$, and Task$_2$ subsequently deallocates Object$_2$.
    244 Allocator-induced passive false-sharing occurs when Object$_2$ is reallocated to Task$_2$ while Task$_1$ is still using Object$_1$.
    245 
    246 
    247 \subsection{Heap Blowup}
    248 \label{s:HeapBlowup}
    249 
    250 In a multi-threaded program, heap blowup can occur when memory freed by one thread is inaccessible to other threads due to the allocation strategy.
    251 Specific examples are presented in later sections.
    252 
    253 
    254 \section{Multi-Threaded Memory-Allocator Features}
    255 \label{s:MultiThreadedMemoryAllocatorFeatures}
    256 
    257 The following features are used in the construction of multi-threaded memory-allocators:
    258 \begin{list}{\arabic{enumi}.}{\usecounter{enumi}\topsep=0.5ex\parsep=0pt\itemsep=0pt}
    259 \item multiple heaps
    260 \begin{list}{\alph{enumii})}{\usecounter{enumii}\topsep=0.5ex\parsep=0pt\itemsep=0pt}
    261 \item with or without a global heap
    262 \item with or without ownership
    263 \end{list}
    264 \item object containers
    265 \begin{list}{\alph{enumii})}{\usecounter{enumii}\topsep=0.5ex\parsep=0pt\itemsep=0pt}
    266 \item with or without ownership
    267 \item fixed or variable sized
    268 \item global or local free-lists
    269 \end{list}
    270 \item hybrid private/public heap
    271 \item allocation buffer
    272 \item lock-free operations
    273 \end{list}
    274 The first feature, multiple heaps, pertains to different kinds of heaps.
    275 The second feature, object containers, pertains to the organization of objects within the storage area.
    276 The remaining features apply to different parts of the allocator design or implementation.
    277 
    278 
    279 \section{Multiple Heaps}
    280 \label{s:MultipleHeaps}
    281 
    282 A multi-threaded allocator has potentially multiple threads and heaps.
    283 The multiple threads cause complexity, and multiple heaps are a mechanism for dealing with the complexity.
    284 The spectrum ranges from multiple threads using a single heap, denoted as T:1 (see \VRef[Figure]{f:SingleHeap}), to multiple threads sharing multiple heaps, denoted as T:H (see \VRef[Figure]{f:SharedHeaps}), to one thread per heap, denoted as 1:1 (see \VRef[Figure]{f:PerThreadHeap}), which is almost back to a single-threaded allocator.
    285 
    286 
    287 \paragraph{T:1 model} where all threads allocate and deallocate objects from one heap.
    288 Memory is obtained from the freed objects, or reserved memory in the heap, or from the operating system (OS);
    289 the heap may also return freed memory to the operating system.
    290 The arrows indicate the direction memory conceptually moves for each kind of operation: allocation moves memory along the path from the heap/operating-system to the user application, while deallocation moves memory along the path from the application back to the heap/operating-system.
    291 To safely handle concurrency, a single heap uses locking to provide mutual exclusion.
    292 Whether using a single lock for all heap operations or fine-grained locking for different operations, a single heap may be a significant source of contention for programs with a large amount of memory allocation.
    293 
    294 \begin{figure}
    295 \centering
    296 \subfigure[T:1]{
    297 %       \input{SingleHeap.pstex_t}
    298         \input{SingleHeap}
    299         \label{f:SingleHeap}
    300 } % subfigure
    301 \vrule
    302 \subfigure[T:H]{
    303 %       \input{MultipleHeaps.pstex_t}
    304         \input{SharedHeaps}
    305         \label{f:SharedHeaps}
    306 } % subfigure
    307 \vrule
    308 \subfigure[1:1]{
    309 %       \input{MultipleHeapsGlobal.pstex_t}
    310         \input{PerThreadHeap}
    311         \label{f:PerThreadHeap}
    312 } % subfigure
    313 \caption{Multiple Heaps, Thread:Heap Relationship}
    314 \end{figure}
    315 
    316 
    317 \paragraph{T:H model} where each thread allocates storage from several heaps depending on certain criteria, with the goal of reducing contention by spreading allocations/deallocations across the heaps.
    318 The decision on when to create a new heap and which heap a thread allocates from depends on the allocator design.
    319 The performance goal is to reduce the ratio of heaps to threads.
    320 In general, locking is required, since more than one thread may concurrently access a heap during its lifetime, but contention is reduced because fewer threads access a specific heap.
    321 
    322 For example, multiple heaps are managed in a pool, starting with a single or a fixed number of heaps that increase\-/decrease depending on contention\-/space issues.
    323 At creation, a thread is associated with a heap from the pool.
    324 When the thread attempts an allocation and its associated heap is locked (contention), it scans for an unlocked heap in the pool.
    325 If an unlocked heap is found, the thread changes its association and uses that heap.
    326 If all heaps are locked, the thread may create a new heap, use it, and then place the new heap into the pool;
    327 or the thread can block waiting for a heap to become available.
    328 While the heap-pool approach often minimizes the number of extant heaps, the worse case can result in more heaps than threads;
    329 \eg if the number of threads is large at startup with many allocations creating a large number of heaps and then the number of threads reduces.
    330 
    331 Threads using multiple heaps need to determine the specific heap to access for an allocation/deallocation, \ie association of thread to heap.
    332 A number of techniques are used to establish this association.
    333 The simplest approach is for each thread to have a pointer to its associated heap (or to administrative information that points to the heap), and this pointer changes if the association changes.
    334 For threading systems with thread-local storage, the heap pointer is created using this mechanism;
    335 otherwise, the heap routines must simulate thread-local storage using approaches like hashing the thread's stack-pointer or thread-id to find its associated heap.
    336 
    337 The storage management for multiple heaps is more complex than for a single heap (see \VRef[Figure]{f:AllocatorComponents}).
    338 \VRef[Figure]{f:MultipleHeapStorage} illustrates the general storage layout for multiple heaps.
    339 Allocated and free objects are labelled by the thread or heap they are associated with.
    340 (Links between free objects are removed for simplicity.)
    341 The management information in the static zone must be able to locate all heaps in the dynamic zone.
    342 The management information for the heaps must reside in the dynamic-allocation zone if there are a variable number.
    343 Each heap in the dynamic zone is composed of a list of a free objects and a pointer to its reserved memory.
    344 An alternative implementation is for all heaps to share one reserved memory, which requires a separate lock for the reserved storage to ensure mutual exclusion when acquiring new memory.
    345 Because multiple threads can allocate/free/reallocate adjacent storage, all forms of false sharing may occur.
    346 Other storage-management options are to use @mmap@ to set aside (large) areas of virtual memory for each heap and suballocate each heap's storage within that area, pushing part of the storage management complexity back to the operating system.
    347 
    348 \begin{figure}
    349 \centering
    350 \input{MultipleHeapsStorage}
    351 \caption{Multiple-Heap Storage}
    352 \label{f:MultipleHeapStorage}
    353 \end{figure}
    354 
    355 Multiple heaps increase external fragmentation as the ratio of heaps to threads increases, which can lead to heap blowup.
    356 The external fragmentation experienced by a program with a single heap is now multiplied by the number of heaps, since each heap manages its own free storage and allocates its own reserved memory.
    357 Additionally, objects freed by one heap cannot be reused by other threads, except indirectly by returning free memory to the operating system, which can be expensive.
    358 (Depending on how the operating system provides dynamic storage to an application, returning storage may be difficult or impossible, \eg the contiguous @sbrk@ area in Unix.)
    359 In the worst case, a program in which objects are allocated from one heap but deallocated to another heap means these freed objects are never reused.
    360 
    361 Adding a \newterm{global heap} (G) attempts to reduce the cost of obtaining/returning memory among heaps (sharing) by buffering storage within the application address-space.
    362 Now, each heap obtains and returns storage to/from the global heap rather than the operating system.
    363 Storage is obtained from the global heap only when a heap allocation cannot be fulfilled, and returned to the global heap when a heap's free memory exceeds some threshold.
    364 Similarly, the global heap buffers this memory, obtaining and returning storage to/from the operating system as necessary.
    365 The global heap does not have its own thread and makes no internal allocation requests;
    366 instead, it uses the application thread, which called one of the multiple heaps and then the global heap, to perform operations.
    367 Hence, the worst-case cost of a memory operation includes all these steps.
    368 With respect to heap blowup, the global heap provides an indirect mechanism to move free memory among heaps, which usually has a much lower cost than interacting with the operating system to achieve the same goal and is independent of the mechanism used by the operating system to present dynamic memory to an address space.
    369 
    370 However, since any thread may indirectly perform a memory operation on the global heap, it is a shared resource that requires locking.
    371 A single lock can be used to protect the global heap or fine-grained locking can be used to reduce contention.
    372 In general, the cost is minimal since the majority of memory operations are completed without the use of the global heap.
    373 
    374 
    375 \paragraph{1:1 model (thread heaps)} where each thread has its own heap eliminating most contention and locking because threads seldom access another thread's heap (see ownership in \VRef{s:Ownership}).
    376 An additional benefit of thread heaps is improved locality due to better memory layout.
    377 As each thread only allocates from its heap, all objects for a thread are consolidated in the storage area for that heap, better utilizing each CPUs cache and accessing fewer pages.
    378 In contrast, the T:H model spreads each thread's objects over a larger area in different heaps.
    379 Thread heaps can also eliminate allocator-induced active false-sharing, if memory is acquired so it does not overlap at crucial boundaries with memory for another thread's heap.
    380 For example, assume page boundaries coincide with cache line boundaries, then if a thread heap always acquires pages of memory, no two threads share a page or cache line unless pointers are passed among them.
    381 Hence, allocator-induced active false-sharing in \VRef[Figure]{f:AllocatorInducedActiveFalseSharing} cannot occur because the memory for thread heaps never overlaps.
    382 
    383 When a thread terminates, there are two options for handling its heap.
    384 First is to free all objects in the heap to the global heap and destroy the thread heap.
    385 Second is to place the thread heap on a list of available heaps and reuse it for a new thread in the future.
    386 Destroying the thread heap immediately may reduce external fragmentation sooner, since all free objects are freed to the global heap and may be reused by other threads.
    387 Alternatively, reusing thread heaps may improve performance if the inheriting thread makes similar allocation requests as the thread that previously held the thread heap because any unfreed storage is immediately accessible..
    388 
    389 
    390 \subsection{User-Level Threading}
    391 
    392 It is possible to use any of the heap models with user-level (M:N) threading.
    393 However, an important goal of user-level threading is for fast operations (creation/termination/context-switching) by not interacting with the operating system, which allows the ability to create large numbers of high-performance interacting threads ($>$ 10,000).
    394 It is difficult to retain this goal, if the user-threading model is directly involved with the heap model.
    395 \VRef[Figure]{f:UserLevelKernelHeaps} shows that virtually all user-level threading systems use whatever kernel-level heap-model is provided by the language runtime.
    396 Hence, a user thread allocates/deallocates from/to the heap of the kernel thread on which it is currently executing.
    397 
    398 \begin{figure}
    399 \centering
    400 \input{UserKernelHeaps}
    401 \caption{User-Level Kernel Heaps}
    402 \label{f:UserLevelKernelHeaps}
    403 \end{figure}
    404 
    405 Adopting this model results in a subtle problem with shared heaps.
    406 With kernel threading, an operation that is started by a kernel thread is always completed by that thread.
    407 For example, if a kernel thread starts an allocation/deallocation on a shared heap, it always completes that operation with that heap even if preempted, \ie any locking correctness associated with the shared heap is preserved across preemption.
    408 
    409 However, this correctness property is not preserved for user-level threading.
    410 A user thread can start an allocation/deallocation on one kernel thread, be preempted (time slice), and continue running on a different kernel thread to complete the operation~\cite{Dice02}.
    411 When the user thread continues on the new kernel thread, it may have pointers into the previous kernel-thread's heap and hold locks associated with it.
    412 To get the same kernel-thread safety, time slicing must be disabled/\-enabled around these operations, so the user thread cannot jump to another kernel thread.
    413 However, eagerly disabling/enabling time-slicing on the allocation/deallocation fast path is expensive, because preemption is rare (10--100 milliseconds).
    414 Instead, techniques exist to lazily detect this case in the interrupt handler, abort the preemption, and return to the operation so it can complete atomically.
    415 Occasionally ignoring a preemption should be benign, but a persistent lack of preemption can result in both short and long term starvation.
    416 
    417 
    418 \begin{figure}
    419 \centering
    420 \subfigure[Ownership]{
    421         \input{MultipleHeapsOwnership}
    422 } % subfigure
    423 \hspace{0.25in}
    424 \subfigure[No Ownership]{
    425         \input{MultipleHeapsNoOwnership}
    426 } % subfigure
    427 \caption{Heap Ownership}
    428 \label{f:HeapsOwnership}
    429 \end{figure}
    430 
    431 
    432 \subsection{Ownership}
    433 \label{s:Ownership}
    434 
    435 \newterm{Ownership} defines which heap an object is returned-to on deallocation.
    436 If a thread returns an object to the heap it was originally allocated from, a heap has ownership of its objects.
    437 Alternatively, a thread can return an object to the heap it is currently associated with, which can be any heap accessible during a thread's lifetime.
    438 \VRef[Figure]{f:HeapsOwnership} shows an example of multiple heaps (minus the global heap) with and without ownership.
    439 Again, the arrows indicate the direction memory conceptually moves for each kind of operation.
    440 For the 1:1 thread:heap relationship, a thread only allocates from its own heap, and without ownership, a thread only frees objects to its own heap, which means the heap is private to its owner thread and does not require any locking, called a \newterm{private heap}.
    441 For the T:1/T:H models with or without ownership or the 1:1 model with ownership, a thread may free objects to different heaps, which makes each heap publicly accessible to all threads, called a \newterm{public heap}.
    442 
    443 \VRef[Figure]{f:MultipleHeapStorageOwnership} shows the effect of ownership on storage layout.
    444 (For simplicity assume the heaps all use the same size of reserves storage.)
    445 In contrast to \VRef[Figure]{f:MultipleHeapStorage}, each reserved area used by a heap only contains free storage for that particular heap because threads must return free objects back to the owner heap.
    446 Again, because multiple threads can allocate/free/reallocate adjacent storage in the same heap, all forms of false sharing may occur.
    447 The exception is for the 1:1 model if reserved memory does not overlap a cache-line because all allocated storage within a used area is associated with a single thread.
    448 In this case, there is no allocator-induced active false-sharing (see \VRef[Figure]{f:AllocatorInducedActiveFalseSharing}) because two adjacent allocated objects used by different threads cannot share a cache-line.
    449 As well, there is no allocator-induced passive false-sharing (see \VRef[Figure]{f:AllocatorInducedActiveFalseSharing}) because two adjacent allocated objects used by different threads cannot occur because free objects are returned to the owner heap.
    450 % Passive false-sharing may still occur, if delayed ownership is used (see below).
    451 
    452 \begin{figure}
    453 \centering
    454 \input{MultipleHeapsOwnershipStorage.pstex_t}
    455 \caption{Multiple-Heap Storage with Ownership}
    456 \label{f:MultipleHeapStorageOwnership}
    457 \end{figure}
    458 
    459 The main advantage of ownership is preventing heap blowup by returning storage for reuse by the owner heap.
    460 Ownership prevents the classical problem where one thread performs allocations from one heap, passes the object to another thread, and the receiving thread deallocates the object to another heap, hence draining the initial heap of storage.
    461 As well, allocator-induced passive false-sharing is eliminated because returning an object to its owner heap means it can never be allocated to another thread.
    462 For example, in \VRef[Figure]{f:AllocatorInducedPassiveFalseSharing}, the deallocation by Task$_2$ returns Object$_2$ back to Task$_1$'s heap;
    463 hence a subsequent allocation by Task$_2$ cannot return this storage.
    464 The disadvantage of ownership is deallocating to another task's heap so heaps are no longer private and require locks to provide safe concurrent access.
    465 
    466 Object ownership can be immediate or delayed, meaning free objects may be batched on a separate free list either by the returning or receiving thread.
    467 While the returning thread can batch objects, batching across multiple heaps is complex and there is no obvious time when to push back to the owner heap.
    468 It is better for returning threads to immediately return to the receiving thread's batch list as the receiving thread has better knowledge when to incorporate the batch list into its free pool.
    469 Batching leverages the fact that most allocation patterns use the contention-free fast-path so locking on the batch list is rare for both the returning and receiving threads.
    470 
    471 It is possible for heaps to steal objects rather than return them and reallocating these objects when storage runs out on a heap.
    472 However, stealing can result in passive false-sharing.
    473 For example, in \VRef[Figure]{f:AllocatorInducedPassiveFalseSharing}, Object$_2$ may be deallocated to Task$_2$'s heap initially.
    474 If Task$_2$ reallocates Object$_2$ before it is returned to its owner heap, then passive false-sharing may occur.
    475 
    476 
    477 \section{Object Containers}
    478 \label{s:ObjectContainers}
    479 
    480 Bracketing every allocation with headers/trailers can result in significant internal fragmentation, as shown in \VRef[Figure]{f:ObjectHeaders}.
    481 Especially if the headers contain redundant management information, \eg object size may be the same for many objects because programs only allocate a small set of object sizes.
    482 As well, it can result in poor cache usage, since only a portion of the cache line is holding useful information from the program's perspective.
    483 Spatial locality can also be negatively affected leading to poor cache locality~\cite{Feng05}:
    484 while the header and object are together in memory, they are generally not accessed together;
    485 \eg the object is accessed by the program when it is allocated, while the header is accessed by the allocator when the object is free.
    486 
    487 \begin{figure}
    488 \centering
    489 \subfigure[Object Headers]{
    490         \input{ObjectHeaders}
    491         \label{f:ObjectHeaders}
    492 } % subfigure
    493 \subfigure[Object Container]{
    494         \input{Container}
    495         \label{f:ObjectContainer}
    496 } % subfigure
    497 \caption{Header Placement}
    498 \label{f:HeaderPlacement}
    499 \end{figure}
    500 
    501 An alternative approach factors common header/trailer information to a separate location in memory and organizes associated free storage into blocks called \newterm{object containers} (\newterm{superblocks} in~\cite{Berger00}), as in \VRef[Figure]{f:ObjectContainer}.
    502 The header for the container holds information necessary for all objects in the container;
    503 a trailer may also be used at the end of the container.
    504 Similar to the approach described for thread heaps in \VRef{s:MultipleHeaps}, if container boundaries do not overlap with memory of another container at crucial boundaries and all objects in a container are allocated to the same thread, allocator-induced active false-sharing is avoided.
    505 
    506 The difficulty with object containers lies in finding the object header/trailer given only the object address, since that is normally the only information passed to the deallocation operation.
    507 One way to do this is to start containers on aligned addresses in memory, then truncate the lower bits of the object address to obtain the header address (or round up and subtract the trailer size to obtain the trailer address).
    508 For example, if an object at address 0xFC28\,EF08 is freed and containers are aligned on 64\,KB (0x0001\,0000) addresses, then the container header is at 0xFC28\,0000.
    509 
    510 Normally, a container has homogeneous objects of fixed size, with fixed information in the header that applies to all container objects (\eg object size and ownership).
    511 This approach greatly reduces internal fragmentation since far fewer headers are required, and potentially increases spatial locality as a cache line or page holds more objects since the objects are closer together due to the lack of headers.
    512 However, although similar objects are close spatially within the same container, different sized objects are further apart in separate containers.
    513 Depending on the program, this may or may not improve locality.
    514 If the program uses several objects from a small number of containers in its working set, then locality is improved since fewer cache lines and pages are required.
    515 If the program uses many containers, there is poor locality, as both caching and paging increase.
    516 Another drawback is that external fragmentation may be increased since containers reserve space for objects that may never be allocated by the program, \ie there are often multiple containers for each size only partially full.
    517 However, external fragmentation can be reduced by using small containers.
    518 
    519 Containers with heterogeneous objects implies different headers describing them, which complicates the problem of locating a specific header solely by an address.
    520 A couple of solutions can be used to implement containers with heterogeneous objects.
    521 However, the problem with allowing objects of different sizes is that the number of objects, and therefore headers, in a single container is unpredictable.
    522 One solution allocates headers at one end of the container, while allocating objects from the other end of the container;
    523 when the headers meet the objects, the container is full.
    524 Freed objects cannot be split or coalesced since this causes the number of headers to change.
    525 The difficulty in this strategy remains in finding the header for a specific object;
    526 in general, a search is necessary to find the object's header among the container headers.
    527 A second solution combines the use of container headers and individual object headers.
    528 Each object header stores the object's heterogeneous information, such as its size, while the container header stores the homogeneous information, such as the owner when using ownership.
    529 This approach allows containers to hold different types of objects, but does not completely separate headers from objects.
    530 The benefit of the container in this case is to reduce some redundant information that is factored into the container header.
    531 
    532 In summary, object containers trade off internal fragmentation for external fragmentation by isolating common administration information to remove/reduce internal fragmentation, but at the cost of external fragmentation as some portion of a container may not be used and this portion is unusable for other kinds of allocations.
    533 A consequence of this tradeoff is its effect on spatial locality, which can produce positive or negative results depending on program access-patterns.
    534 
    535 
    536 \subsection{Container Ownership}
    537 \label{s:ContainerOwnership}
    538 
    539 Without ownership, objects in a container are deallocated to the heap currently associated with the thread that frees the object.
    540 Thus, different objects in a container may be on different heap free-lists (see \VRef[Figure]{f:ContainerNoOwnershipFreelist}).
    541 With ownership, all objects in a container belong to the same heap (see \VRef[Figure]{f:ContainerOwnershipFreelist}), so ownership of an object is determined by the container owner.
    542 If multiple threads can allocate/free/reallocate adjacent storage in the same heap, all forms of false sharing may occur.
    543 Only with the 1:1 model and ownership is active and passive false-sharing avoided (see \VRef{s:Ownership}).
    544 Passive false-sharing may still occur, if delayed ownership is used.
    545 Finally, a completely free container can become reserved storage and be reset to allocate objects of a new size or freed to the global heap.
    546 
    547 \begin{figure}
    548 \centering
    549 \subfigure[No Ownership]{
    550         \input{ContainerNoOwnershipFreelist}
    551         \label{f:ContainerNoOwnershipFreelist}
    552 } % subfigure
    553 \vrule
    554 \subfigure[Ownership]{
    555         \input{ContainerOwnershipFreelist}
    556         \label{f:ContainerOwnershipFreelist}
    557 } % subfigure
    558 \caption{Free-list Structure with Container Ownership}
    559 \end{figure}
    560 
    561 When a container changes ownership, the ownership of all objects within it change as well.
    562 Moving a container involves moving all objects on the heap's free-list in that container to the new owner.
    563 This approach can reduce contention for the global heap, since each request for objects from the global heap returns a container rather than individual objects.
    564 
    565 Additional restrictions may be applied to the movement of containers to prevent active false-sharing.
    566 For example, in \VRef[Figure]{f:ContainerFalseSharing1}, a container being used by Task$_1$ changes ownership, through the global heap.
    567 In \VRef[Figure]{f:ContainerFalseSharing2}, when Task$_2$ allocates an object from the newly acquired container it is actively false-sharing even though no objects are passed among threads.
    568 Note, once the object is freed by Task$_1$, no more false sharing can occur until the container changes ownership again.
    569 To prevent this form of false sharing, container movement may be restricted to when all objects in the container are free.
    570 One implementation approach that increases the freedom to return a free container to the operating system involves allocating containers using a call like @mmap@, which allows memory at an arbitrary address to be returned versus only storage at the end of the contiguous @sbrk@ area, again pushing storage management complexity back to the operating system.
    571 
    572 \begin{figure}
    573 \centering
    574 \subfigure[]{
    575         \input{ContainerFalseSharing1}
    576         \label{f:ContainerFalseSharing1}
    577 } % subfigure
    578 \subfigure[]{
    579         \input{ContainerFalseSharing2}
    580         \label{f:ContainerFalseSharing2}
    581 } % subfigure
    582 \caption{Active False-Sharing using Containers}
    583 \label{f:ActiveFalseSharingContainers}
    584 \end{figure}
    585 
    586 Using containers with ownership increases external fragmentation since a new container for a requested object size must be allocated separately for each thread requesting it.
    587 In \VRef[Figure]{f:ExternalFragmentationContainerOwnership}, using object ownership allocates 80\% more space than without ownership.
    588 
    589 \begin{figure}
    590 \centering
    591 \subfigure[No Ownership]{
    592         \input{ContainerNoOwnership}
    593 } % subfigure
    594 \\
    595 \subfigure[Ownership]{
    596         \input{ContainerOwnership}
    597 } % subfigure
    598 \caption{External Fragmentation with Container Ownership}
    599 \label{f:ExternalFragmentationContainerOwnership}
    600 \end{figure}
    601 
    602 
    603 \subsection{Container Size}
    604 \label{s:ContainerSize}
    605 
    606 One way to control the external fragmentation caused by allocating a large container for a small number of requested objects is to vary the size of the container.
    607 As described earlier, container boundaries need to be aligned on addresses that are a power of two to allow easy location of the header (by truncating lower bits).
    608 Aligning containers in this manner also determines the size of the container.
    609 However, the size of the container has different implications for the allocator.
    610 
    611 The larger the container, the fewer containers are needed, and hence, the fewer headers need to be maintained in memory, improving both internal fragmentation and potentially performance.
    612 However, with more objects in a container, there may be more objects that are unallocated, increasing external fragmentation.
    613 With smaller containers, not only are there more containers, but a second new problem arises where objects are larger than the container.
    614 In general, large objects, \eg greater than 64\,KB, are allocated directly from the operating system and are returned immediately to the operating system to reduce long-term external fragmentation.
    615 If the container size is small, \eg 1\,KB, then a 1.5\,KB object is treated as a large object, which is likely to be inappropriate.
    616 Ideally, it is best to use smaller containers for smaller objects, and larger containers for medium objects, which leads to the issue of locating the container header.
    617 
    618 In order to find the container header when using different sized containers, a super container is used (see~\VRef[Figure]{f:SuperContainers}).
    619 The super container spans several containers, contains a header with information for finding each container header, and starts on an aligned address.
    620 Super-container headers are found using the same method used to find container headers by dropping the lower bits of an object address.
    621 The containers within a super container may be different sizes or all the same size.
    622 If the containers in the super container are different sizes, then the super-container header must be searched to determine the specific container for an object given its address.
    623 If all containers in the super container are the same size, \eg 16KB, then a specific container header can be found by a simple calculation.
    624 The free space at the end of a super container is used to allocate new containers.
    625 
    626 \begin{figure}
    627 \centering
    628 \input{SuperContainers}
    629 % \includegraphics{diagrams/supercontainer.eps}
    630 \caption{Super Containers}
    631 \label{f:SuperContainers}
    632 \end{figure}
    633 
    634 Minimal internal and external fragmentation is achieved by having as few containers as possible, each being as full as possible.
    635 It is also possible to achieve additional benefit by using larger containers for popular small sizes, as it reduces the number of containers with associated headers.
    636 However, this approach assumes it is possible for an allocator to determine in advance which sizes are popular.
    637 Keeping statistics on requested sizes allows the allocator to make a dynamic decision about which sizes are popular.
    638 For example, after receiving a number of allocation requests for a particular size, that size is considered a popular request size and larger containers are allocated for that size.
    639 If the decision is incorrect, larger containers than necessary are allocated that remain mostly unused.
    640 A programmer may be able to inform the allocator about popular object sizes, using a mechanism like @mallopt@, in order to select an appropriate container size for each object size.
    641 
    642 
    643 \subsection{Container Free-Lists}
    644 \label{s:containersfreelists}
    645 
    646 The container header allows an alternate approach for managing the heap's free-list.
    647 Rather than maintain a global free-list throughout the heap (see~\VRef[Figure]{f:GlobalFreeListAmongContainers}), the containers are linked through their headers and only the local free objects within a container are linked together (see~\VRef[Figure]{f:LocalFreeListWithinContainers}).
    648 Note, maintaining free lists within a container assumes all free objects in the container are associated with the same heap;
    649 thus, this approach only applies to containers with ownership.
    650 
    651 This alternate free-list approach can greatly reduce the complexity of moving all freed objects belonging to a container to another heap.
    652 To move a container using a global free-list, as in \VRef[Figure]{f:GlobalFreeListAmongContainers}, the free list is first searched to find all objects within the container.
    653 Each object is then removed from the free list and linked together to form a local free-list for the move to the new heap.
    654 With local free-lists in containers, as in \VRef[Figure]{f:LocalFreeListWithinContainers}, the container is simply removed from one heap's free list and placed on the new heap's free list.
    655 Thus, when using local free-lists, the operation of moving containers is reduced from $O(N)$ to $O(1)$.
    656 The cost is adding information to a header, which increases the header size, and therefore internal fragmentation.
    657 
    658 \begin{figure}
    659 \centering
    660 \subfigure[Global Free-List Among Containers]{
    661         \input{FreeListAmongContainers}
    662         \label{f:GlobalFreeListAmongContainers}
    663 } % subfigure
    664 \hspace{0.25in}
    665 \subfigure[Local Free-List Within Containers]{
    666         \input{FreeListWithinContainers}
    667         \label{f:LocalFreeListWithinContainers}
    668 } % subfigure
    669 \caption{Container Free-List Structure}
    670 \label{f:ContainerFreeListStructure}
    671 \end{figure}
    672 
    673 When all objects in the container are the same size, a single free-list is sufficient.
    674 However, when objects in the container are different size, the header needs a free list for each size class when using a binning allocation algorithm, which can be a significant increase in the container-header size.
    675 The alternative is to use a different allocation algorithm with a single free-list, such as a sequential-fit allocation-algorithm.
    676 
    677 
    678 \subsection{Hybrid Private/Public Heap}
    679 \label{s:HybridPrivatePublicHeap}
    680 
    681 Section~\Vref{s:Ownership} discusses advantages and disadvantages of public heaps (T:H model and with ownership) and private heaps (thread heaps with ownership).
    682 For thread heaps with ownership, it is possible to combine these approaches into a hybrid approach with both private and public heaps (see~\VRef[Figure]{f:HybridPrivatePublicHeap}).
    683 The main goal of the hybrid approach is to eliminate locking on thread-local allocation/deallocation, while providing ownership to prevent heap blowup.
    684 In the hybrid approach, a task first allocates from its private heap and second from its public heap if no free memory exists in the private heap.
    685 Similarly, a task first deallocates an object its private heap, and second to the public heap.
    686 Both private and public heaps can allocate/deallocate to/from the global heap if there is no free memory or excess free memory, although an implementation may choose to funnel all interaction with the global heap through one of the heaps.
    687 Note, deallocation from the private to the public (dashed line) is unlikely because there is no obvious advantages unless the public heap provides the only interface to the global heap.
    688 Finally, when a task frees an object it does not own, the object is either freed immediately to its owner's public heap or put in the freeing task's private heap for delayed ownership, which allows the freeing task to temporarily reuse an object before returning it to its owner or batch objects for an owner heap into a single return.
    689 
    690 \begin{figure}
    691 \centering
    692 \input{PrivatePublicHeaps.pstex_t}
    693 \caption{Hybrid Private/Public Heap for Per-thread Heaps}
    694 \label{f:HybridPrivatePublicHeap}
    695 % \vspace{10pt}
    696 % \input{RemoteFreeList.pstex_t}
    697 % \caption{Remote Free-List}
    698 % \label{f:RemoteFreeList}
    699 \end{figure}
    700 
    701 As mentioned, an implementation may have only one heap interact with the global heap, so the other heap can be simplified.
    702 For example, if only the private heap interacts with the global heap, the public heap can be reduced to a lock-protected free-list of objects deallocated by other threads due to ownership, called a \newterm{remote free-list}.
    703 To avoid heap blowup, the private heap allocates from the remote free-list when it reaches some threshold or it has no free storage.
    704 Since the remote free-list is occasionally cleared during an allocation, this adds to that cost.
    705 Clearing the remote free-list is $O(1)$ if the list can simply be added to the end of the private-heap's free-list, or $O(N)$ if some action must be performed for each freed object.
    706 
    707 If only the public heap interacts with other threads and the global heap, the private heap can handle thread-local allocations and deallocations without locking.
    708 In this scenario, the private heap must deallocate storage after reaching a certain threshold to the public heap (and then eventually to the global heap from the public heap) or heap blowup can occur.
    709 If the public heap does the major management, the private heap can be simplified to provide high-performance thread-local allocations and deallocations.
    710 
    711 The main disadvantage of each thread having both a private and public heap is the complexity of managing two heaps and their interactions in an allocator.
    712 Interestingly, heap implementations often focus on either a private or public heap, giving the impression a single versus a hybrid approach is being used.
    713 In many case, the hybrid approach is actually being used, but the simpler heap is just folded into the complex heap, even though the operations logically belong in separate heaps.
    714 For example, a remote free-list is actually a simple public-heap, but may be implemented as an integral component of the complex private-heap in an allocator, masking the presence of a hybrid approach.
    715 
    716 
    717 \section{Allocation Buffer}
    718 \label{s:AllocationBuffer}
    719 
    720 An allocation buffer is reserved memory (see~\VRef{s:AllocatorComponents}) not yet allocated to the program, and is used for allocating objects when the free list is empty.
    721 That is, rather than requesting new storage for a single object, an entire buffer is requested from which multiple objects are allocated later.
    722 Any heap may use an allocation buffer, resulting in allocation from the buffer before requesting objects (containers) from the global heap or operating system, respectively.
    723 The allocation buffer reduces contention and the number of global/operating-system calls.
    724 For coalescing, a buffer is split into smaller objects by allocations, and recomposed into larger buffer areas during deallocations.
    725 
    726 Allocation buffers are useful initially when there are no freed objects in a heap because many allocations usually occur when a thread starts (simple bump allocation).
    727 Furthermore, to prevent heap blowup, objects should be reused before allocating a new allocation buffer.
    728 Thus, allocation buffers are often allocated more frequently at program/thread start, and then allocations often diminish.
    729 
    730 Using an allocation buffer with a thread heap avoids active false-sharing, since all objects in the allocation buffer are allocated to the same thread.
    731 For example, if all objects sharing a cache line come from the same allocation buffer, then these objects are allocated to the same thread, avoiding active false-sharing.
    732 Active false-sharing may still occur if objects are freed to the global heap and reused by another heap.
    733 
    734 Allocation buffers may increase external fragmentation, since some memory in the allocation buffer may never be allocated.
    735 A smaller allocation buffer reduces the amount of external fragmentation, but increases the number of calls to the global heap or operating system.
    736 The allocation buffer also slightly increases internal fragmentation, since a pointer is necessary to locate the next free object in the buffer.
    737 
    738 The unused part of a container, neither allocated or freed, is an allocation buffer.
    739 For example, when a container is created, rather than placing all objects within the container on the free list, the objects form an allocation buffer and are allocated from the buffer as allocation requests are made.
    740 This lazy method of constructing objects is beneficial in terms of paging and caching.
    741 For example, although an entire container, possibly spanning several pages, is allocated from the operating system, only a small part of the container is used in the working set of the allocator, reducing the number of pages and cache lines that are brought into higher levels of cache.
    742 
    743 
    744 \section{Lock-Free Operations}
    745 \label{s:LockFreeOperations}
    746 
    747 A \newterm{lock-free algorithm} guarantees safe concurrent-access to a data structure, so that at least one thread makes progress, but an individual task has no execution bound and may starve~\cite[pp.~745--746]{Herlihy93}.
    748 (A \newterm{wait-free algorithm} puts a bound on the number of steps any thread takes to complete an operation to prevent starvation.)
    749 Lock-free operations can be used in an allocator to reduce or eliminate the use of locks.
    750 While locks and lock-free data-structures often have equal performance, lock-free has the advantage of not holding a lock across preemption so other threads can continue to make progress.
    751 With respect to the heap, these situations are unlikely unless all threads make extremely high use of dynamic-memory allocation, which can be an indication of poor design.
    752 Nevertheless, lock-free algorithms can reduce the number of context switches, since a thread does not yield/block while waiting for a lock;
    753 on the other hand, a thread may busy-wait for an unbounded period holding a processor.
    754 Finally, lock-free implementations have greater complexity and hardware dependency.
    755 Lock-free algorithms can be applied most easily to simple free-lists, \eg remote free-list, to allow lock-free insertion and removal from the head of a stack.
    756 Implementing lock-free operations for more complex data-structures (queue~\cite{Valois94}/deque~\cite{Sundell08}) is correspondingly more complex.
    757 Michael~\cite{Michael04} and Gidenstam \etal \cite{Gidenstam05} have created lock-free variations of the Hoard allocator.
     63\paragraph{larson}
     64(FIX ME: cite benchmark and hoard) Larson simulates a server environment. Multiple threads are created where each thread allocator and free a number of objects within a size range. Some objects are passed from threads to the child threads to free. It caluculates memory operations per second as an indicator of memory allocator's performance.
  • doc/theses/mubeen_zulfiqar_MMath/benchmarks.tex

    r92538ab r4559b34  
    4141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    4242
    43 
    44 \section{Benchmarks}
    45 There are multiple benchmarks that are built individually and evaluate different aspects of a memory allocator. But, there is not standard set of benchamrks that can be used to evaluate multiple aspects of memory allocators.
    46 
    47 \paragraph{threadtest}
    48 (FIX ME: cite benchmark and hoard) Each thread repeatedly allocates and then deallocates 100,000 objects. Runtime of the benchmark evaluates its efficiency.
    49 
    50 \paragraph{shbench}
    51 (FIX ME: cite benchmark and hoard) Each thread allocates and randomly frees a number of random-sized objects. It is a stress test that also uses runtime to determine efficiency of the allocator.
    52 
    53 \paragraph{larson}
    54 (FIX ME: cite benchmark and hoard) Larson simulates a server environment. Multiple threads are created where each thread allocator and free a number of objects within a size range. Some objects are passed from threads to the child threads to free. It caluculates memory operations per second as an indicator of memory allocator's performance.
    55 
    56 
    5743\section{Performance Matrices of Memory Allocators}
    5844
  • doc/theses/mubeen_zulfiqar_MMath/intro.tex

    r92538ab r4559b34  
    11\chapter{Introduction}
    22
    3 % Shared-memory multi-processor computers are ubiquitous and important for improving application performance.
    4 % However, writing programs that take advantage of multiple processors is not an easy task~\cite{Alexandrescu01b}, \eg shared resources can become a bottleneck when increasing (scaling) threads.
    5 % One crucial shared resource is program memory, since it is used by all threads in a shared-memory concurrent-program~\cite{Berger00}.
    6 % Therefore, providing high-performance, scalable memory-management is important for virtually all shared-memory multi-threaded programs.
    7 
    8 \vspace*{-23pt}
    9 Memory management takes a sequence of program generated allocation/deallocation requests and attempts to satisfy them within a fixed-sized block of memory while minimizing the total amount of memory used.
    10 A general-purpose dynamic-allocation algorithm cannot anticipate future allocation requests so its output is rarely optimal.
    11 However, memory allocators do take advantage of regularities in allocation patterns for typical programs to produce excellent results, both in time and space (similar to LRU paging).
    12 In general, allocators use a number of similar techniques, each optimizing specific allocation patterns.
    13 Nevertheless, memory allocators are a series of compromises, occasionally with some static or dynamic tuning parameters to optimize specific program-request patterns.
    14 
    15 
    16 \section{Memory Structure}
    17 \label{s:MemoryStructure}
    18 
    19 \VRef[Figure]{f:ProgramAddressSpace} shows the typical layout of a program's address space divided into the following zones (right to left): static code/data, dynamic allocation, dynamic code/data, and stack, with free memory surrounding the dynamic code/data~\cite{memlayout}.
    20 Static code and data are placed into memory at load time from the executable and are fixed-sized at runtime.
    21 Dynamic-allocation memory starts empty and grows/shrinks as the program dynamically creates/deletes variables with independent lifetime.
    22 The programming-language's runtime manages this area, where management complexity is a function of the mechanism for deleting variables.
    23 Dynamic code/data memory is managed by the dynamic loader for libraries loaded at runtime, which is complex especially in a multi-threaded program~\cite{Huang06}.
    24 However, changes to the dynamic code/data space are typically infrequent, many occurring at program startup, and are largely outside of a program's control.
    25 Stack memory is managed by the program call-mechanism using a simple LIFO technique, which works well for sequential programs.
    26 For multi-threaded programs (and coroutines), a new stack is created for each thread;
    27 these thread stacks are commonly created in dynamic-allocation memory.
    28 This thesis focuses on management of the dynamic-allocation memory.
    29 
    30 \begin{figure}
    31 \centering
    32 \input{AddressSpace}
    33 \vspace{-5pt}
    34 \caption{Program Address Space Divided into Zones}
    35 \label{f:ProgramAddressSpace}
    36 \end{figure}
    37 
    38 
    39 \section{Dynamic Memory-Management}
    40 \label{s:DynamicMemoryManagement}
    41 
    42 Modern programming languages manage dynamic-allocation memory in different ways.
    43 Some languages, such as Lisp~\cite{CommonLisp}, Java~\cite{Java}, Haskell~\cite{Haskell}, Go~\cite{Go}, provide explicit allocation but \emph{implicit} deallocation of data through garbage collection~\cite{Wilson92}.
    44 In general, garbage collection supports memory compaction, where dynamic (live) data is moved during runtime to better utilize space.
    45 However, moving data requires finding pointers to it and updating them to reflect new data locations.
    46 Programming languages such as C~\cite{C}, \CC~\cite{C++}, and Rust~\cite{Rust} provide the programmer with explicit allocation \emph{and} deallocation of data.
    47 These languages cannot find and subsequently move live data because pointers can be created to any storage zone, including internal components of allocated objects, and may contain temporary invalid values generated by pointer arithmetic.
    48 Attempts have been made to perform quasi garbage collection in C/\CC~\cite{Boehm88}, but it is a compromise.
    49 This thesis only examines dynamic memory-management with \emph{explicit} deallocation.
    50 While garbage collection and compaction are not part this work, many of the work's results are applicable to the allocation phase in any memory-management approach.
    51 
    52 Most programs use a general-purpose allocator, often the one provided implicitly by the programming-language's runtime.
    53 When this allocator proves inadequate, programmers often write specialize allocators for specific needs.
    54 C and \CC allow easy replacement of the default memory allocator with an alternative specialized or general-purpose memory-allocator.
    55 (Jikes RVM MMTk~\cite{MMTk} provides a similar generalization for the Java virtual machine.)
    56 However, high-performance memory-allocators for kernel and user multi-threaded programs are still being designed and improved.
    57 For this reason, several alternative general-purpose allocators have been written for C/\CC with the goal of scaling in a multi-threaded program~\cite{Berger00,mtmalloc,streamflow,tcmalloc}.
    58 This thesis examines the design of high-performance allocators for use by kernel and user multi-threaded applications written in C/\CC.
    59 
    60 
    61 \section{Contributions}
    62 \label{s:Contributions}
    63 
    64 This work provides the following contributions in the area of concurrent dynamic allocation:
    65 \begin{enumerate}[leftmargin=*]
    66 \item
    67 Implementation of a new stand-lone concurrent low-latency memory-allocator ($\approx$1,200 lines of code) for C/\CC programs using kernel threads (1:1 threading), and specialized versions of the allocator for the programming languages \uC and \CFA using user-level threads running over multiple kernel threads (M:N threading).
    68 
    69 \item
    70 Adopt @nullptr@ return for a zero-sized allocation, rather than an actual memory address, which can be passed to @free@.
    71 
    72 \item
    73 Extend the standard C heap functionality by preserving with each allocation:
    74 \begin{itemize}[itemsep=0pt]
    75 \item
    76 its request size plus the amount allocated,
    77 \item
    78 whether an allocation is zero fill,
    79 \item
    80 and allocation alignment.
    81 \end{itemize}
    82 
    83 \item
    84 Use the preserved zero fill and alignment as \emph{sticky} properties for @realloc@ to zero-fill and align when storage is extended or copied.
    85 Without this extension, it is unsafe to @realloc@ storage initially allocated with zero-fill/alignment as these properties are not preserved when copying.
    86 This silent generation of a problem is unintuitive to programmers and difficult to locate because it is transient.
    87 
    88 \item
    89 Provide additional heap operations to complete programmer expectation with respect to accessing different allocation properties.
    90 \begin{itemize}
    91 \item
    92 @resize( oaddr, size )@ re-purpose an old allocation for a new type \emph{without} preserving fill or alignment.
    93 \item
    94 @resize( oaddr, alignment, size )@ re-purpose an old allocation with new alignment but \emph{without} preserving fill.
    95 \item
    96 @realloc( oaddr, alignment, size )@ same as @realloc@ but adding or changing alignment.
    97 \item
    98 @aalloc( dim, elemSize )@ same as @calloc@ except memory is \emph{not} zero filled.
    99 \item
    100 @amemalign( alignment, dim, elemSize )@ same as @aalloc@ with memory alignment.
    101 \item
    102 @cmemalign( alignment, dim, elemSize )@ same as @calloc@ with memory alignment.
    103 \end{itemize}
    104 
    105 \item
    106 Provide additional heap wrapper functions in \CFA creating an orthogonal set of allocation operations and properties.
    107 
    108 \item
    109 Provide additional query operations to access information about an allocation:
    110 \begin{itemize}
    111 \item
    112 @malloc_alignment( addr )@ returns the alignment of the allocation pointed-to by @addr@.
    113 If the allocation is not aligned or @addr@ is the @nulladdr@, the minimal alignment is returned.
    114 \item
    115 @malloc_zero_fill( addr )@ returns a boolean result indicating if the memory pointed-to by @addr@ is allocated with zero fill, e.g., by @calloc@/@cmemalign@.
    116 \item
    117 @malloc_size( addr )@ returns the size of the memory allocation pointed-to by @addr@.
    118 \item
    119 @malloc_usable_size( addr )@ returns the usable (total) size of the memory pointed-to by @addr@, i.e., the bin size containing the allocation, where @malloc_size( addr )@ $\le$ @malloc_usable_size( addr )@.
    120 \end{itemize}
    121 
    122 \item
    123 Provide mostly contention-free allocation and free operations via a heap-per-kernel-thread implementation.
    124 
    125 \item
    126 Provide complete, fast, and contention-free allocation statistics to help understand program behaviour:
    127 \begin{itemize}
    128 \item
    129 @malloc_stats()@ print memory-allocation statistics on the file-descriptor set by @malloc_stats_fd@.
    130 \item
    131 @malloc_info( options, stream )@ print memory-allocation statistics as an XML string on the specified file-descriptor set by @malloc_stats_fd@.
    132 \item
    133 @malloc_stats_fd( fd )@ set file-descriptor number for printing memory-allocation statistics (default @STDERR_FILENO@).
    134 This file descriptor is used implicitly by @malloc_stats@ and @malloc_info@.
    135 \end{itemize}
    136 
    137 \item
    138 Provide extensive runtime checks to valid allocation operations and identify the amount of unfreed storage at program termination.
    139 
    140 \item
    141 Build 4 different versions of the allocator:
    142 \begin{itemize}
    143 \item
    144 static or dynamic linking
    145 \item
    146 statistic/debugging (testing) or no statistic/debugging (performance)
    147 \end{itemize}
    148 A program may link to any of these 4 versions of the allocator often without recompilation.
    149 (It is possible to separate statistics and debugging, giving 8 different versions.)
    150 
    151 \item
    152 A micro-benchmark test-suite for comparing allocators rather than relying on a suite of arbitrary programs.
    153 These micro-benchmarks have adjustment knobs to simulate allocation patterns hard-coded into arbitrary test programs
    154 \end{enumerate}
    155 
    156 \begin{comment}
    1573\noindent
    1584====================
     
    18026
    18127\section{Introduction}
    182 Dynamic memory allocation and management is one of the core features of C. It gives programmer the freedom to allocate, free, use, and manage dynamic memory himself. The programmer is not given the complete control of the dynamic memory management instead an interface of memory allocator is given to the programmer that can be used to allocate/free dynamic memory for the application's use.
     28Dynamic memory allocation and management is one of the core features of C. It gives programmer the freedom to allocate, free, use, and manage dynamic memory himself. The programmer is not given the complete control of the dynamic memory management instead an interface of memory allocator is given to the progrmmer that can be used to allocate/free dynamic memory for the application's use.
    18329
    184 Memory allocator is a layer between the programmer and the system. Allocator gets dynamic memory from the system in heap/mmap area of application storage and manages it for programmer's use.
     30Memory allocator is a layer between thr programmer and the system. Allocator gets dynamic memory from the system in heap/mmap area of application storage and manages it for programmer's use.
    18531
    186 GNU C Library (FIX ME: cite this) provides an interchangeable memory allocator that can be replaced with a custom memory allocator that supports required features and fulfills application's custom needs. It also allows others to innovate in memory allocation and design their own memory allocator. GNU C Library has set guidelines that should be followed when designing a stand-alone memory allocator. GNU C Library requires new memory allocators to have at lease following set of functions in their allocator's interface:
     32GNU C Library (FIX ME: cite this) provides an interchangeable memory allocator that can be replaced with a custom memory allocator that supports required features and fulfills application's custom needs. It also allows others to innovate in memory allocation and design their own memory allocator. GNU C Library has set guidelines that should be followed when designing a standalone memory allocator. GNU C Library requires new memory allocators to have atlease following set of functions in their allocator's interface:
    18733
    18834\begin{itemize}
     
    19743\end{itemize}
    19844
    199 In addition to the above functions, GNU C Library also provides some more functions to increase the usability of the dynamic memory allocator. Most stand-alone allocators also provide all or some of the above additional functions.
     45In addition to the above functions, GNU C Library also provides some more functions to increase the usability of the dynamic memory allocator. Most standalone allocators also provide all or some of the above additional functions.
    20046
    20147\begin{itemize}
     
    21460\end{itemize}
    21561
    216 With the rise of concurrent applications, memory allocators should be able to fulfill dynamic memory requests from multiple threads in parallel without causing contention on shared resources. There needs to be a set of a standard benchmarks that can be used to evaluate an allocator's performance in different scenarios.
     62With the rise of concurrent applications, memory allocators should be able to fulfill dynamic memory requests from multiple threads in parallel without causing contention on shared resources. There needs to be a set of a standard benchmarks that can be used to evaluate an allocator's performance in different scenerios.
    21763
    21864\section{Research Objectives}
     
    22369Design a lightweight concurrent memory allocator with added features and usability that are currently not present in the other memory allocators.
    22470\item
    225 Design a suite of benchmarks to evaluate multiple aspects of a memory allocator.
     71Design a suite of benchmarks to evalute multiple aspects of a memory allocator.
    22672\end{itemize}
    22773
    22874\section{An outline of the thesis}
    22975LAST FIX ME: add outline at the end
    230 \end{comment}
  • doc/theses/mubeen_zulfiqar_MMath/performance.tex

    r92538ab r4559b34  
    1818\noindent
    1919====================
    20 
    21 \section{Machine Specification}
    22 
    23 The performance experiments were run on three different multicore systems to determine if there is consistency across platforms:
    24 \begin{itemize}
    25 \item
    26 AMD EPYC 7662, 64-core socket $\times$ 2, 2.0 GHz
    27 \item
    28 Huawei ARM TaiShan 2280 V2 Kunpeng 920, 24-core socket $\times$ 4, 2.6 GHz
    29 \item
    30 Intel Xeon Gold 5220R, 48-core socket $\times$ 2, 2.20GHz
    31 \end{itemize}
    32 
    33 
    34 \section{Existing Memory Allocators}
    35 With dynamic allocation being an important feature of C, there are many stand-alone memory allocators that have been designed for different purposes. For this thesis, we chose 7 of the most popular and widely used memory allocators.
    36 
    37 \paragraph{dlmalloc}
    38 dlmalloc (FIX ME: cite allocator) is a thread-safe allocator that is single threaded and single heap. dlmalloc maintains free-lists of different sizes to store freed dynamic memory. (FIX ME: cite wasik)
    39 
    40 \paragraph{hoard}
    41 Hoard (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and using a heap layer framework. It has per-thread heaps that have thread-local free-lists, and a global shared heap. (FIX ME: cite wasik)
    42 
    43 \paragraph{jemalloc}
    44 jemalloc (FIX ME: cite allocator) is a thread-safe allocator that uses multiple arenas. Each thread is assigned an arena. Each arena has chunks that contain contagious memory regions of same size. An arena has multiple chunks that contain regions of multiple sizes.
    45 
    46 \paragraph{ptmalloc}
    47 ptmalloc (FIX ME: cite allocator) is a modification of dlmalloc. It is a thread-safe multi-threaded memory allocator that uses multiple heaps. ptmalloc heap has similar design to dlmalloc's heap.
    48 
    49 \paragraph{rpmalloc}
    50 rpmalloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses per-thread heap. Each heap has multiple size-classes and each size-class contains memory regions of the relevant size.
    51 
    52 \paragraph{tbb malloc}
    53 tbb malloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses private heap for each thread. Each private-heap has multiple bins of different sizes. Each bin contains free regions of the same size.
    54 
    55 \paragraph{tc malloc}
    56 tcmalloc (FIX ME: cite allocator) is a thread-safe allocator. It uses per-thread cache to store free objects that prevents contention on shared resources in multi-threaded application. A central free-list is used to refill per-thread cache when it gets empty.
    57 
    5820
    5921\section{Memory Allocators}
  • doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.bib

    r92538ab r4559b34  
    3434    year          = "2008"
    3535}
    36 
    37 @article{Sleator85,
    38     author      = {Sleator, Daniel Dominic and Tarjan, Robert Endre},
    39     title       = {Self-Adjusting Binary Search Trees},
    40     journal     = jacm,
    41     volume      = 32,
    42     number      = 3,
    43     year        = 1985,
    44     issn        = {0004-5411},
    45     pages       = {652-686},
    46     doi         = {http://doi.acm.org.proxy.lib.uwaterloo.ca/10.1145/3828.3835},
    47     address     = {New York, NY, USA},
    48 }
    49 
    50 @article{Berger00,
    51     author      = {Emery D. Berger and Kathryn S. McKinley and Robert D. Blumofe and Paul R. Wilson},
    52     title       = {Hoard: A Scalable Memory Allocator for Multithreaded Applications},
    53     booktitle   = {International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS-IX)},
    54     journal     = sigplan,
    55     volume      = 35,
    56     number      = 11,
    57     month       = nov,
    58     year        = 2000,
    59     pages       = {117-128},
    60     note        = {International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS-IX)},
    61 }
    62 
    63 @inproceedings{berger02reconsidering,
    64     author      = {Emery D. Berger and Benjamin G. Zorn and Kathryn S. McKinley},
    65     title       = {Reconsidering Custom Memory Allocation},
    66     booktitle   = {Proceedings of the 17th ACM SIGPLAN Conference on Object-Oriented Programming: Systems, Languages, and Applications (OOPSLA) 2002},
    67     month       = nov,
    68     year        = 2002,
    69     location    = {Seattle, Washington, USA},
    70     publisher   = {ACM},
    71     address     = {New York, NY, USA},
    72 }
    73 
    74 @article{larson99memory,
    75     author      = {Per-{\AA}ke Larson and Murali Krishnan},
    76     title       = {Memory Allocation for Long-Running Server Applications},
    77     journal     = sigplan,
    78     volume      = 34,
    79     number      = 3,
    80     pages       = {176-185},
    81     year        = 1999,
    82     url         = {http://citeseer.ist.psu.edu/article/larson98memory.html}
    83 }
    84 
    85 @techreport{gidpt04,
    86     author      = {Anders Gidenstam and Marina Papatriantafilou and Philippas Tsigas},
    87     title       = {Allocating Memory in a Lock-Free Manner},
    88     number      = {2004-04},
    89     institution = {Computing Science},
    90     address     = {Chalmers University of Technology},
    91     year        = 2004,
    92     url         = {http://citeseer.ist.psu.edu/gidenstam04allocating.html}
    93 }
    94 
    95 @phdthesis{berger02thesis,
    96     author      = {Emery Berger},
    97     title       = {Memory Management for High-Performance Applications},
    98     school      = {The University of Texas at Austin},
    99     year        = 2002,
    100     month       = aug,
    101     url         = {http://citeseer.ist.psu.edu/article/berger02memory.html}
    102 }
    103 
    104 @misc{sgimisc,
    105     author      = {SGI},
    106     title       = {The Standard Template Library for {C++}},
    107     note        = {\textsf{www.sgi.com/\-tech/\-stl/\-Allocators.html}},
    108 }
    109 
    110 @misc{dlmalloc,
    111     author      = {Doug Lea},
    112     title       = {dlmalloc version 2.8.4},
    113     month       = may,
    114     year        = 2009,
    115     note        = {\textsf{ftp://g.oswego.edu/\-pub/\-misc/\-malloc.c}},
    116 }
    117 
    118 @misc{ptmalloc2,
    119     author      = {Wolfram Gloger},
    120     title       = {ptmalloc version 2},
    121     month       = jun,
    122     year        = 2006,
    123     note        = {\textsf{http://www.malloc.de/\-malloc/\-ptmalloc2-current.tar.gz}},
    124 }
    125 
    126 @misc{nedmalloc,
    127     author      = {Niall Douglas},
    128     title       = {nedmalloc version 1.06 Beta},
    129     month       = jan,
    130     year        = 2010,
    131     note        = {\textsf{http://\-prdownloads.\-sourceforge.\-net/\-nedmalloc/\-nedmalloc\_v1.06beta1\_svn1151.zip}},
    132 }
    133 
    134 @misc{hoard,
    135     author      = {Emery D. Berger},
    136     title       = {hoard version 3.8},
    137     month       = nov,
    138     year        = 2009,
    139     note        = {\textsf{http://www.cs.umass.edu/\-$\sim$emery/\-hoard/\-hoard-3.8/\-source/hoard-38.tar.gz}},
    140 }
    141 
    142 @comment{mtmalloc,
    143     author      = {Greg Nakhimovsky},
    144     title       = {Improving Scalability of Multithreaded Dynamic Memory Allocation},
    145     journal     = {Dr. Dobb's},
    146     month       = jul,
    147     year        = 2001,
    148     url         = {http://www.ddj.com/mobile/184404685?pgno=1}
    149 }
    150 
    151 @misc{mtmalloc,
    152     key         = {mtmalloc},
    153     title       = {mtmalloc.c},
    154     year        = 2009,
    155     note        = {\textsf{http://src.opensolaris.org/\-source/\-xref/\-onnv/\-onnv-gate/\-usr/\-src/\-lib/\-libmtmalloc/\-common/\-mtmalloc.c}},
    156 }
    157 
    158 @misc{tcmalloc,
    159     author      = {Sanjay Ghemawat and Paul Menage},
    160     title       = {tcmalloc version 1.5},
    161     month       = jan,
    162     year        = 2010,
    163     note        = {\textsf{http://google-perftools.\-googlecode.\-com/\-files/\-google-perftools-1.5.tar.gz}},
    164 }
    165 
    166 @inproceedings{streamflow,
    167     author      = {Scott Schneider and Christos D. Antonopoulos and Dimitrios S. Nikolopoulos},
    168     title       = {Scalable Locality-Conscious Multithreaded Memory Allocation},
    169     booktitle   = {International Symposium on Memory Management (ISSM'06)},
    170     month       = jun,
    171     year        = 2006,
    172     pages       = {84-94},
    173     location    = {Ottawa, Ontario, Canada},
    174     publisher   = {ACM},
    175     address     = {New York, NY, USA},
    176 }
    177 
    178 @misc{streamflowweb,
    179     author      = {Scott Schneider and Christos Antonopoulos and Dimitrios Nikolopoulos},
    180     title       = {Streamflow},
    181     note        = {\textsf{http://people.cs.vt.edu/\-\char`\~scschnei/\-streamflow}},
    182 }
    183 
    184 @inproceedings{Blumofe94,
    185     author      = {R. Blumofe and C. Leiserson},
    186     title       = {Scheduling Multithreaded Computations by Work Stealing},
    187     booktitle   = {Proceedings of the 35th Annual Symposium on Foundations of Computer Science, Santa Fe, New Mexico.},
    188     pages       = {356-368},
    189     year        = 1994,
    190     month       = nov,
    191     url         = {http://citeseer.ist.psu.edu/article/blumofe94scheduling.html}
    192 }
    193 
    194 @article{Johnstone99,
    195     author      = {Mark S. Johnstone and Paul R. Wilson},
    196     title       = {The Memory Fragmentation Problem: Solved?},
    197     journal     = sigplan,
    198     volume      = 34,
    199     number      = 3,
    200     pages       = {26-36},
    201     year        = 1999,
    202 }
    203 
    204 @inproceedings{Grunwald93,
    205     author      = {Dirk Grunwald and Benjamin G. Zorn and Robert Henderson},
    206     title       = {Improving the Cache Locality of Memory Allocation},
    207     booktitle   = {{SIGPLAN} Conference on Programming Language Design and Implementation},
    208     pages       = {177-186},
    209     year        = 1993,
    210     url         = {http://citeseer.ist.psu.edu/grunwald93improving.html}
    211 }
    212 
    213 @inproceedings{Wilson95,
    214     author      = {Wilson, Paul R. and Johnstone, Mark S. and Neely, Michael and Boles, David},
    215     title       = {Dynamic Storage Allocation: A Survey and Critical Review},
    216     booktitle   = {Proc. Int. Workshop on Memory Management},
    217     address     = {Kinross Scotland, UK},
    218     year        = 1995,
    219     url         = {http://citeseer.ist.psu.edu/wilson95dynamic.html}
    220 }
    221 
    222 @inproceedings{Siebert00,
    223     author      = {Fridtjof Siebert},
    224     title       = {Eliminating External Fragmentation in a Non-moving Garbage Collector for Java},
    225     booktitle   = {CASES '00: Proceedings of the 2000 international conference on Compilers, architecture, and synthesis for embedded systems},
    226     year        = 2000,
    227     isbn        = {1-58113-338-3},
    228     pages       = {9-17},
    229     location    = {San Jose, California, United States},
    230     doi         = {http://doi.acm.org.proxy.lib.uwaterloo.ca/10.1145/354880.354883},
    231     publisher   = {ACM Press},
    232     address     = {New York, NY, USA}
    233 }
    234 
    235 @inproceedings{Lim98,
    236    author       = {Tian F. Lim and Przemyslaw Pardyak and Brian N. Bershad},
    237    title        = {A Memory-Efficient Real-Time Non-copying Garbage Collector},
    238    booktitle    = {ISMM '98: Proceedings of the 1st international symposium on Memory management},
    239    year         = 1998,
    240    isbn         = {1-58113-114-3},
    241    pages        = {118-129},
    242    location     = {Vancouver, British Columbia, Canada},
    243    doi          = {http://doi.acm.org.proxy.lib.uwaterloo.ca/10.1145/286860.286873},
    244    publisher    = {ACM Press},
    245    address      = {New York, NY, USA}
    246 }
    247 
    248 @article{Chang01,
    249     author      = {J. Morris Chang and Woo Hyong Lee and Witawas Srisa-an},
    250     title       = {A Study of the Allocation Behavior of {C++} Programs},
    251     journal     = {J. Syst. Softw.},
    252     volume      = 57,
    253     number      = 2,
    254     year        = 2001,
    255     issn        = {0164-1212},
    256     pages       = {107-118},
    257     doi         = {http://dx.doi.org/10.1016/S0164-1212(00)00122-9},
    258     publisher   = {Elsevier Science Inc.},
    259     address     = {New York, NY, USA}
    260 }
    261 
    262 @article{Herlihy93,
    263     author      = {Maurice Herlihy},
    264     title       = {A Methodology for Implementing Highly Concurrent Data Objects},
    265     journal     = toplas,
    266     volume      = 15,
    267     number      = 5,
    268     year        = 1993,
    269     issn        = {0164-0925},
    270     pages       = {745-770},
    271     doi         = {http://doi.acm.org.proxy.lib.uwaterloo.ca/10.1145/161468.161469},
    272     publisher   = {ACM Press},
    273     address     = {New York, NY, USA}
    274 }
    275 
    276 @article{Denning05,
    277     author      = {Peter J. Denning},
    278     title       = {The Locality Principle},
    279     journal     = cacm,
    280     volume      = 48,
    281     number      = 7,
    282     year        = 2005,
    283     issn        = {0001-0782},
    284     pages       = {19-24},
    285     doi         = {http://doi.acm.org.proxy.lib.uwaterloo.ca/10.1145/1070838.1070856},
    286     publisher   = {ACM Press},
    287     address     = {New York, NY, USA}
    288 }
    289 
    290 @misc{wilson-locality,
    291     author      = {Paul R. Wilson},
    292     title       = {Locality of Reference, Patterns in Program Behavior, Memory Management, and Memory Hierarchies},
    293     url         = {http://citeseer.ist.psu.edu/337869.html}
    294 }
    295 
    296 @inproceedings{Feng05,
    297     author      = {Yi Feng and Emery D. Berger},
    298     title       = {A Locality-Improving Dynamic Memory Allocator},
    299     booktitle   = {Proceedings of the 2005 Workshop on Memory System Performance},
    300     location    = {Chicago, Illinois},
    301     publisher   = {ACM},
    302     address     = {New York, NY, USA},
    303     month       = jun,
    304     year        = 2005,
    305     pages       = {68-77},
    306 }
    307 
    308 @inproceedings{grunwald-locality,
    309     author      = {Dirk Grunwald and Benjamin Zorn and Robert Henderson},
    310     title       = {Improving the Cache Locality of Memory Allocation},
    311     booktitle   = {PLDI '93: Proceedings of the ACM SIGPLAN 1993 conference on Programming language design and implementation},
    312     year        = 1993,
    313     isbn        = {0-89791-598-4},
    314     pages       = {177-186},
    315     location    = {Albuquerque, New Mexico, United States},
    316     doi         = {http://doi.acm.org.proxy.lib.uwaterloo.ca/10.1145/155090.155107},
    317     publisher   = {ACM Press},
    318     address     = {New York, NY, USA}
    319 }
    320 
    321 @article{Alexandrescu01b,
    322     author      = {Andrei Alexandrescu},
    323     title       = {{volatile} -- Multithreaded Programmer's Best Friend},
    324     journal     = {Dr. Dobb's},
    325     month       = feb,
    326     year        = 2001,
    327     url         = {http://www.ddj.com/cpp/184403766}
    328 }
    329 
    330 @article{Attardi03,
    331     author      = {Joseph Attardi and Neelakanth Nadgir},
    332     title       = {A Comparison of Memory Allocators in Multiprocessors},
    333     journal     = {Sun Developer Network},
    334     month       = jun,
    335     year        = 2003,
    336     note        = {\textsf{http://developers.sun.com/\-solaris/\-articles/\-multiproc/\-multiproc.html}},
    337 }
    338 
    339 @unpublished{memlayout,
    340     author      = {Peter Jay Salzman},
    341     title       = {Memory Layout and the Stack},
    342     journal     = {Using GNU's GDB Debugger},
    343     note        = {\textsf{http://dirac.org/\-linux/\-gdb/\-02a-Memory\_Layout\_And\_The\_Stack.php}},
    344 }
    345 
    346 @unpublished{Ferguson07,
    347     author      = {Justin N. Ferguson},
    348     title       = {Understanding the Heap by Breaking It},
    349     note        = {\textsf{https://www.blackhat.com/\-presentations/\-bh-usa-07/Ferguson/\-Whitepaper/\-bh-usa-07-ferguson-WP.pdf}},
    350 }
    351 
    352 @inproceedings{Huang06,
    353     author      = {Xianglong Huang and Brian T Lewis and Kathryn S McKinley},
    354     title       = {Dynamic Code Management: Improving Whole Program Code Locality in Managed Runtimes},
    355     booktitle   = {VEE '06: Proceedings of the 2nd international conference on Virtual execution environments},
    356     year        = 2006,
    357     isbn        = {1-59593-332-6},
    358     pages       = {133-143},
    359     location    = {Ottawa, Ontario, Canada},
    360     doi         = {http://doi.acm.org/10.1145/1134760.1134779},
    361     publisher   = {ACM Press},
    362     address     = {New York, NY, USA}
    363  }
    364 
    365 @inproceedings{Herlihy03,
    366     author      = {M. Herlihy and V. Luchangco and M. Moir},
    367     title       = {Obstruction-free Synchronization: Double-ended Queues as an Example},
    368     booktitle   = {Proceedings of the 23rd IEEE International Conference on Distributed Computing Systems},
    369     year        = 2003,
    370     month       = may,
    371     url         = {http://www.cs.brown.edu/~mph/publications.html}
    372 }
    373 
    374 @techreport{Detlefs93,
    375     author      = {David L. Detlefs and Al Dosser and Benjamin Zorn},
    376     title       = {Memory Allocation Costs in Large {C} and {C++} Programs},
    377     number      = {CU-CS-665-93},
    378     institution = {University of Colorado},
    379     address     = {130 Lytton Avenue, Palo Alto, CA 94301 and Campus Box 430, Boulder, CO 80309},
    380     year        = 1993,
    381     url         = {http://citeseer.ist.psu.edu/detlefs93memory.html}
    382 }
    383 
    384 @inproceedings{Oyama99,
    385     author      = {Y. Oyama and K. Taura and A. Yonezawa},
    386     title       = {Executing Parallel Programs With Synchronization Bottlenecks Efficiently},
    387     booktitle   = {Proceedings of International Workshop on Parallel and Distributed Computing for Symbolic and Irregular Applications (PDSIA '99)},
    388     year        = {1999},
    389     pages       = {182--204},
    390     publisher   = {World Scientific},
    391     address     = {Sendai, Japan},
    392 }
    393 
    394 @inproceedings{Dice02,
    395     author      = {Dave Dice and Alex Garthwaite},
    396     title       = {Mostly Lock-Free Malloc},
    397     booktitle   = {Proceedings of the 3rd international symposium on Memory management (ISMM'02)},
    398     month       = jun,
    399     year        = 2002,
    400     pages       = {163-174},
    401     location    = {Berlin, Germany},
    402     publisher   = {ACM},
    403     address     = {New York, NY, USA},
    404 }
  • doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.tex

    r92538ab r4559b34  
    6060% For hyperlinked PDF, suitable for viewing on a computer, use this:
    6161\documentclass[letterpaper,12pt,titlepage,oneside,final]{book}
    62 \usepackage[T1]{fontenc}        % Latin-1 => 256-bit characters, => | not dash, <> not Spanish question marks
    6362
    6463% For PDF, suitable for double-sided printing, change the PrintVersion variable below to "true" and use this \documentclass line instead of the one above:
     
    8685\usepackage{comment} % Removes large sections of the document.
    8786\usepackage{tabularx}
    88 \usepackage{subfigure}
    89 
    90 \usepackage{algorithm}
    91 \usepackage{algpseudocode}
    9287
    9388% Hyperlinks make it very easy to navigate an electronic document.
     
    172167\input{common}
    173168%\usepackageinput{common}
    174 \CFAStyle                                               % CFA code-style
    175 \lstset{language=CFA}                                   % default language
    176 \lstset{basicstyle=\linespread{0.9}\sf}                 % CFA typewriter font
    177 \newcommand{\uC}{$\mu$\CC}
     169\CFAStyle                                               % CFA code-style for all languages
     170\lstset{basicstyle=\linespread{0.9}\tt}                 % CFA typewriter font
    178171\newcommand{\PAB}[1]{{\color{red}PAB: #1}}
    179172
     
    231224\addcontentsline{toc}{chapter}{\textbf{References}}
    232225
    233 \bibliography{pl,uw-ethesis}
     226\bibliography{uw-ethesis,pl}
    234227% Tip: You can create multiple .bib files to organize your references.
    235228% Just list them all in the \bibliogaphy command, separated by commas (no spaces).
  • doc/theses/thierry_delisle_PhD/thesis/Makefile

    r92538ab r4559b34  
    2929PICTURES = ${addsuffix .pstex, \
    3030        base \
    31         base_avg \
    3231        empty \
    3332        emptybit \
     
    3938        system \
    4039        cycle \
    41         result.cycle.jax.ops \
    4240}
    4341
     
    114112        python3 $< $@
    115113
    116 build/result.%.ns.svg : data/% | ${Build}
    117         ../../../../benchmark/plot.py -f $< -o $@ -y "ns per ops"
    118 
    119 build/result.%.ops.svg : data/% | ${Build}
    120         ../../../../benchmark/plot.py -f $< -o $@ -y "Ops per second"
    121 
    122114## pstex with inverted colors
    123115%.dark.pstex : fig/%.fig Makefile | ${Build}
  • doc/theses/thierry_delisle_PhD/thesis/fig/base.fig

    r92538ab r4559b34  
    8989         5700 5210 5550 4950 5250 4950 5100 5210 5250 5470 5550 5470
    9090         5700 5210
    91 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
    92          3600 5700 3600 1200
    93 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
    94          4800 5700 4800 1200
    95 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
    96          6000 5700 6000 1200
    97914 2 -1 50 -1 0 12 0.0000 2 135 630 2100 3075 Threads\001
    98924 2 -1 50 -1 0 12 0.0000 2 165 450 2100 2850 Ready\001
  • doc/theses/thierry_delisle_PhD/thesis/glossary.tex

    r92538ab r4559b34  
    101101
    102102\longnewglossaryentry{at}
    103 {name={task}}
     103{name={fred}}
    104104{
    105105Abstract object representing an unit of work. Systems will offer one or more concrete implementations of this concept (\eg \gls{kthrd}, \gls{job}), however, most of the concept of schedulings are independent of the particular implementations of the work representation. For this reason, this document use the term \Gls{at} to mean any representation and not one in particular.
  • doc/theses/thierry_delisle_PhD/thesis/text/core.tex

    r92538ab r4559b34  
    33Before discussing scheduling in general, where it is important to address systems that are changing states, this document discusses scheduling in a somewhat ideal scenario, 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 resources necessary to accomplish the work, \eg, enough workers. In short, the system is neither overloaded nor underloaded.
    44
    5 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 this new load and return to the steady state, \eg, by adding or removing workers. Therefore, flaws in scheduling the steady state tend 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 this new load and return to the steady state, \eg, by adding or removing workers. Therefore, flaws in scheduling the steady state can to be pervasive in all states.
    66
    77\section{Design Goals}
     
    2525It 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 share the limited hardware resources. Therefore, the guarantee is considered respected if a \gls{thrd} gets access to a \emph{fair share} of the hardware resources, even if that share is very small.
    2626
    27 Similarly the performance guarantee, the lack of interference among threads, is only relevant up to a point. Ideally, the cost of running and blocking should 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 attempts to show the cost of scheduling is at worst equivalent to existing algorithms used in popular languages. This demonstration can be made by comparing applications built in \CFA to applications built with other languages or other models. Recall programmer expectation is that the impact of the scheduler can be ignored. Therefore, if the cost of scheduling is compatitive to other popular languages, the guarantee will be consider achieved.
     27Similarly the performance guarantee, the lack of interference among threads, is only relevant up to a point. Ideally, the cost of running and blocking should 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 attempts to show the cost of scheduling is at worst equivalent to existing algorithms used in popular languages. This demonstration can be made by comparing applications built in \CFA to applications built with other languages or other models. Recall programmer expectation is that the impact of the scheduler can be ignored. Therefore, if the cost of scheduling is equivalent to or lower than other popular languages, I consider the guarantee achieved.
    2828
    2929More precisely the scheduler should be:
     
    3333\end{itemize}
    3434
    35 \subsection{Fairness vs Scheduler Locality} \label{fairnessvlocal}
     35\subsection{Fairness vs Scheduler Locality}
    3636An important performance factor in modern architectures is cache locality. Waiting for data at lower levels or not present in the cache can have a major impact on performance. Having multiple \glspl{hthrd} writing to the same cache lines also leads to cache lines that must be waited on. It is therefore preferable to divide data among each \gls{hthrd}\footnote{This partitioning can be an explicit division up front or using data structures where different \glspl{hthrd} are naturally routed to different cache lines.}.
    3737
    38 For a scheduler, having good locality\footnote{This section discusses \emph{internal locality}, \ie, the locality of the data used by the scheduler versus \emph{external locality}, \ie, how the data used by the application is affected by scheduling. External locality is a much more complicated subject and is discussed in the next section.}, \ie, having the data 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 a \gls{thrd}, and as consequence cache lines, to a \gls{hthrd} that is currently available.
     38For a scheduler, having good locality\footnote{This section discusses \emph{internal locality}, \ie, the locality of the data used by the scheduler versus \emph{external locality}, \ie, how the data used by the application is affected by scheduling. External locality is a much more complicated subject and is discussed in part~\ref{Evaluation} on evaluation.}, \ie, having the data 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 a \gls{thrd}, and as consequence cache lines, to a \gls{hthrd} that is currently available.
    3939
    4040However, I claim that in practice it is possible to strike a balance between fairness and performance because these goals do not necessarily overlap temporally, where Figure~\ref{fig:fair} shows a visual representation of this behaviour. As mentioned, some unfairness is acceptable; therefore it is desirable to have an algorithm that prioritizes cache locality as long as thread delay does not exceed the execution mental-model.
     
    4848\end{figure}
    4949
    50 \subsection{Performance Challenges}\label{pref:challenge}
    51 While there exists a multitude of potential scheduling algorithms, they generally always have to contend with the same performance challenges. Since these challenges are recurring themes in the design of a scheduler it is relevant to describe the central ones here before looking at the design.
    52 
    53 \subsubsection{Scalability}
    54 The most basic performance challenge of a scheduler is scalability.
    55 Given a large number of \procs and an even larger number of \ats, scalability measures how fast \procs can enqueue and dequeues \ats.
    56 One could expect that doubling the number of \procs would double the rate at which \ats are dequeued, but contention on the internal data structure of the scheduler can lead to worst improvements.
    57 While the ready-queue itself can be sharded to alleviate the main source of contention, auxillary scheduling features, \eg counting ready \ats, can also be sources of contention.
    58 
    59 \subsubsection{Migration Cost}
    60 Another important source of latency in scheduling is migration.
    61 An \at is said to have migrated if it is executed by two different \proc consecutively, which is the process discussed in \ref{fairnessvlocal}.
    62 Migrations can have many different causes, but it certain programs it can be all but impossible to limit migrations.
    63 Chapter~\ref{microbench} for example, has a benchmark where any \at can potentially unblock any other \at, which can leat to \ats migrating more often than not.
    64 Because of this it is important to design the internal data structures of the scheduler to limit the latency penalty from migrations.
    65 
    66 
    67 \section{Inspirations}
     50\section{Design}
    6851In general, a na\"{i}ve \glsxtrshort{fifo} ready-queue does not scale with increased parallelism from \glspl{hthrd}, resulting in decreased performance. The problem is adding/removing \glspl{thrd} is a single point of contention. As shown in the evaluation sections, most production schedulers do scale when adding \glspl{hthrd}. The solution to this problem is to shard the ready-queue : create multiple sub-ready-queues that multiple \glspl{hthrd} can access and modify without interfering.
    6952
    70 Before going into the design of \CFA's scheduler proper, it is relevant to discuss two sharding solutions which served as the inspiration scheduler in this thesis.
     53Before going into the design of \CFA's scheduler proper, I want to discuss two sharding solutions which served as the inspiration scheduler in this thesis.
    7154
    7255\subsection{Work-Stealing}
    7356
    74 As mentioned in \ref{existing:workstealing}, a popular pattern shard the ready-queue is work-stealing.
    75 In this pattern each \gls{proc} has its own local ready-queue and \glspl{proc} only access each other's ready-queue if they run out of work on their local ready-queue.
    76 The interesting aspect of workstealing happen in easier scheduling cases, \ie enough work for everyone but no more and no load balancing needed.
    77 In these cases, work-stealing is close to optimal scheduling: it can achieve perfect locality and have no contention.
     57As I mentioned in \ref{existing:workstealing}, a popular pattern shard the ready-queue is work-stealing. As mentionned, in this pattern each \gls{proc} has its own ready-queue and \glspl{proc} only access each other's ready-queue if they run out of work.
     58The interesting aspect of workstealing happen in easier scheduling cases, \ie enough work for everyone but no more and no load balancing needed. In these cases, work-stealing is close to optimal scheduling: it can achieve perfect locality and have no contention.
    7859On the other hand, work-stealing schedulers only attempt to do load-balancing when a \gls{proc} runs out of work.
    79 This means that the scheduler never balances unfair loads unless they result in a \gls{proc} running out of work.
     60This means that the scheduler may never balance unfairness that does not result in a \gls{proc} running out of work.
    8061Chapter~\ref{microbench} shows that in pathological cases this problem can lead to indefinite starvation.
    8162
    8263
    83 Based on these observation, the conclusion is that a \emph{perfect} scheduler should behave very similarly to work-stealing in the easy cases, but should have more proactive load-balancing if the need arises.
     64Based on these observation, I conclude that \emph{perfect} scheduler should behave very similarly to work-stealing in the easy cases, but should have more proactive load-balancing if the need arises.
    8465
    8566\subsection{Relaxed-Fifo}
    8667An entirely different scheme is to create a ``relaxed-FIFO'' queue as in \todo{cite Trevor's paper}. This approach forgos any ownership between \gls{proc} and ready-queue, and simply creates a pool of ready-queues from which the \glspl{proc} can pick from.
    8768\Glspl{proc} choose ready-queus at random, but timestamps are added to all elements of the queue and dequeues are done by picking two queues and dequeing the oldest element.
    88 All subqueues are protected by TryLocks and \procs simply pick a different subqueue if they fail to acquire the TryLock.
    8969The result is a queue that has both decent scalability and sufficient fairness.
    9070The lack of ownership means that as long as one \gls{proc} is still able to repeatedly dequeue elements, it is unlikely that any element will stay on the queue for much longer than any other element.
     
    9575
    9676While the fairness, of this scheme is good, it does suffer in terms of performance.
    97 It requires very wide sharding, \eg at least 4 queues per \gls{hthrd}, and finding non-empty queues can be difficult if there are too few ready \ats.
     77It requires very wide sharding, \eg at least 4 queues per \gls{hthrd}, and the randomness means locality can suffer significantly and finding non-empty queues can be difficult.
    9878
    99 \section{Relaxed-FIFO++}
    100 Since it has inherent fairness quelities and decent performance in the presence of many \ats, the relaxed-FIFO queue appears as a good candidate to form the basis of a scheduler.
    101 The most obvious problems is for workloads where the number of \ats is barely greater than the number of \procs.
    102 In these situations, the wide sharding means most of the sub-queues from which the relaxed queue is formed will be empty.
    103 The consequence is that when a dequeue operations attempts to pick a sub-queue at random, it is likely that it picks an empty sub-queue and will have to pick again.
    104 This problem can repeat an unbounded number of times.
    105 
    106 As this is the most obvious challenge, it is worth addressing first.
    107 The obvious solution is to supplement each subqueue with some sharded data structure that keeps track of which subqueues are empty.
    108 This data structure can take many forms, for example simple bitmask or a binary tree that tracks which branch are empty.
    109 Following a binary tree on each pick has fairly good Big O complexity and many modern architectures have powerful bitmask manipulation instructions.
    110 However, precisely tracking which sub-queues are empty is actually fundamentally problematic.
    111 The reason is that each subqueues are already a form of sharding and the sharding width has presumably already chosen to avoid contention.
    112 However, tracking which ready queue is empty is only useful if the tracking mechanism uses denser sharding than the sub queues, then it will invariably create a new source of contention.
    113 But if the tracking mechanism is not denser than the sub-queues, then it will generally not provide useful because reading this new data structure risks being as costly as simply picking a sub-queue at random.
    114 Early experiments with this approach have shown that even with low success rates, randomly picking a sub-queue can be faster than a simple tree walk.
    115 
    116 The exception to this rule is using local tracking.
    117 If each \proc keeps track locally of which sub-queue is empty, then this can be done with a very dense data structure without introducing a new source of contention.
    118 The consequence of local tracking however, is that the information is not complete.
    119 Each \proc is only aware of the last state it saw each subqueues but does not have any information about freshness.
    120 Even on systems with low \gls{hthrd} count, \eg 4 or 8, this can quickly lead to the local information being no better than the random pick.
    121 This is due in part to the cost of this maintaining this information and its poor quality.
    122 
    123 However, using a very low cost approach to local tracking may actually be beneficial.
    124 If the local tracking is no more costly than the random pick, than \emph{any} improvement to the succes rate, however low it is, would lead to a performance benefits.
    125 This leads to the following approach:
    126 
    127 \subsection{Dynamic Entropy}\cit{https://xkcd.com/2318/}
    128 The Relaxed-FIFO approach can be made to handle the case of mostly empty sub-queues by tweaking the \glsxtrlong{prng}.
    129 The \glsxtrshort{prng} state can be seen as containing a list of all the future sub-queues that will be accessed.
    130 While this is not particularly useful on its own, the consequence is that if the \glsxtrshort{prng} algorithm can be run \emph{backwards}, then the state also contains a list of all the subqueues that were accessed.
    131 Luckily, bidirectional \glsxtrshort{prng} algorithms do exist, for example some Linear Congruential Generators\cit{https://en.wikipedia.org/wiki/Linear\_congruential\_generator} support running the algorithm backwards while offering good quality and performance.
    132 This particular \glsxtrshort{prng} can be used as follows:
    133 
    134 Each \proc maintains two \glsxtrshort{prng} states, which whill be refered to as \texttt{F} and \texttt{B}.
    135 
    136 When a \proc attempts to dequeue a \at, it picks the subqueues by running the \texttt{B} backwards.
    137 When a \proc attempts to enqueue a \at, it runs \texttt{F} forward to pick to subqueue to enqueue to.
    138 If the enqueue is successful, the state \texttt{B} is overwritten with the content of \texttt{F}.
    139 
    140 The result is that each \proc will tend to dequeue \ats that it has itself enqueued.
    141 When most sub-queues are empty, this technique increases the odds of finding \ats at very low cost, while also offering an improvement on locality in many cases.
    142 
    143 However, while this approach does notably improve performance in many cases, this algorithm is still not competitive with work-stealing algorithms.
    144 The fundamental problem is that the constant randomness limits how much locality the scheduler offers.
    145 This becomes problematic both because the scheduler is likely to get cache misses on internal data-structures and because migration become very frequent.
    146 Therefore since the approach of modifying to relaxed-FIFO algorithm to behave more like work stealing does not seem to pan out, the alternative is to do it the other way around.
    147 
    148 \section{Work Stealing++}
    149 To add stronger fairness guarantees to workstealing a few changes.
    150 First, the relaxed-FIFO algorithm has fundamentally better fairness because each \proc always monitors all subqueues.
    151 Therefore the workstealing algorithm must be prepended with some monitoring.
    152 Before attempting to dequeue from a \proc's local queue, the \proc must make some effort to make sure remote queues are not being neglected.
    153 To make this possible, \procs must be able to determie which \at has been on the ready-queue the longest.
    154 Which is the second aspect that much be added.
    155 The relaxed-FIFO approach uses timestamps for each \at and this is also what is done here.
    156 
     79\section{\CFA}
     80The \CFA is effectively attempting to merge these two approaches, keeping the best of both.
     81It is based on the
    15782\begin{figure}
    15883        \centering
    15984        \input{base.pstex_t}
    160         \caption[Base \CFA design]{Base \CFA design \smallskip\newline A Pool of sub-ready queues offers the sharding, two per \glspl{proc}. Each \gls{proc} have local subqueues, however \glspl{proc} can access any of the sub-queues. Each \at is timestamped when enqueued.}
     85        \caption[Base \CFA design]{Base \CFA design \smallskip\newline A list of sub-ready queues offers the sharding, two per \glspl{proc}. However, \glspl{proc} can access any of the sub-queues.}
    16186        \label{fig:base}
    16287\end{figure}
    163 The algorithm is structure as shown in Figure~\ref{fig:base}.
    164 This is very similar to classic workstealing except the local queues are placed in an array so \procs can access eachother's queue in constant time.
    165 Sharding width can be adjusted based on need.
    166 When a \proc attempts to dequeue a \at, it first picks a random remote queue and compares its timestamp to the timestamps of the local queue(s), dequeue from the remote queue if needed.
    167 
    168 Implemented as as naively state above, this approach has some obvious performance problems.
    169 First, it is necessary to have some damping effect on helping.
    170 Random effects like cache misses and preemption can add spurious but short bursts of latency for which helping is not helpful, pun intended.
    171 The effect of these bursts would be to cause more migrations than needed and make this workstealing approach slowdown to the match the relaxed-FIFO approach.
    172 
    173 \begin{figure}
    174         \centering
    175         \input{base_avg.pstex_t}
    176         \caption[\CFA design with Moving Average]{\CFA design with Moving Average \smallskip\newline A moving average is added to each subqueue.}
    177         \label{fig:base-ma}
    178 \end{figure}
    179 
    180 A simple solution to this problem is to compare an exponential moving average\cit{https://en.wikipedia.org/wiki/Moving\_average\#Exponential\_moving\_average} instead if the raw timestamps, shown in Figure~\ref{fig:base-ma}.
    181 Note that this is slightly more complex than it sounds because since the \at at the head of a subqueue is still waiting, its wait time has not ended.
    182 Therefore the exponential moving average is actually an exponential moving average of how long each already dequeued \at have waited.
    183 To compare subqueues, the timestamp at the head must be compared to the current time, yielding the bestcase wait time for the \at at the head of the queue.
    184 This new waiting is averaged with the stored average.
    185 To limit even more the amount of unnecessary migration, a bias can be added to the local queue, where a remote queue is helped only if its moving average is more than \emph{X} times the local queue's average.
    186 None of the experimentation that I have run with these scheduler seem to indicate that the choice of the weight for the moving average or the choice of bis is particularly important.
    187 Weigths and biases of similar \emph{magnitudes} have similar effects.
    188 
    189 With these additions to workstealing, scheduling can be made as fair as the relaxed-FIFO approach, well avoiding the majority of unnecessary migrations.
    190 Unfortunately, the performance of this approach does suffer in the cases with no risks of starvation.
    191 The problem is that the constant polling of remote subqueues generally entail a cache miss.
    192 To make things worst, remote subqueues that are very active, \ie \ats are frequently enqueued and dequeued from them, the higher the chances are that polling will incurr a cache-miss.
    193 Conversly, the active subqueues do not benefit much from helping since starvation is already a non-issue.
    194 This puts this algorithm in an akward situation where it is paying for a cost, but the cost itself suggests the operation was unnecessary.
    195 The good news is that this problem can be mitigated
    196 
    197 \subsection{Redundant Timestamps}
    198 The problem with polling remote queues is due to a tension between the consistency requirement on the subqueue.
    199 For the subqueues, correctness is critical. There must be a consensus among \procs on which subqueues hold which \ats.
    200 Since the timestamps are use for fairness, it is alco important to have consensus and which \at is the oldest.
    201 However, when deciding if a remote subqueue is worth polling, correctness is much less of a problem.
    202 Since the only need is that a subqueue will eventually be polled, some data staleness can be acceptable.
    203 This leads to a tension where stale timestamps are only problematic in some cases.
    204 Furthermore, stale timestamps can be somewhat desirable since lower freshness requirements means less tension on the cache coherence protocol.
    20588
    20689
    207 \begin{figure}
    208         \centering
    209         % \input{base_ts2.pstex_t}
    210         \caption[\CFA design with Redundant Timestamps]{\CFA design with Redundant Timestamps \smallskip\newline A array is added containing a copy of the timestamps. These timestamps are written to with relaxed atomics, without fencing, leading to fewer cache invalidations.}
    211         \label{fig:base-ts2}
    212 \end{figure}
    213 A solution to this is to create a second array containing a copy of the timestamps and average.
    214 This copy is updated \emph{after} the subqueue's critical sections using relaxed atomics.
    215 \Glspl{proc} now check if polling is needed by comparing the copy of the remote timestamp instead of the actual timestamp.
    216 The result is that since there is no fencing, the writes can be buffered and cause fewer cache invalidations.
    21790
    218 The correctness argument here is somewhat subtle.
    219 The data used for deciding whether or not to poll a queue can be stale as long as it does not cause starvation.
    220 Therefore, it is acceptable if stale data make queues appear older than they really are but not fresher.
    221 For the timestamps, this means that missing writes to the timestamp is acceptable since they will make the head \at look older.
    222 For the moving average, as long as the operation are RW-safe, the average is guaranteed to yield a value that is between the oldest and newest values written.
    223 Therefore this unprotected read of the timestamp and average satisfy the limited correctness that is required.
     91% The common solution to the single point of contention is to shard the ready-queue so each \gls{hthrd} can access the ready-queue without contention, increasing performance.
    22492
    225 \subsection{Per CPU Sharding}
     93% \subsection{Sharding} \label{sec:sharding}
     94% An interesting approach to sharding a queue is presented in \cit{Trevors paper}. This algorithm presents a queue with a relaxed \glsxtrshort{fifo} guarantee using an array of strictly \glsxtrshort{fifo} sublists as shown in Figure~\ref{fig:base}. Each \emph{cell} of the array has a timestamp for the last operation and a pointer to a linked-list with a lock. Each node in the list is marked with a timestamp indicating when it is added to the list. A push operation is done by picking a random cell, acquiring the list lock, and pushing to the list. If the cell is locked, the operation is simply retried on another random cell until a lock is acquired. A pop operation is done in a similar fashion except two random cells are picked. If both cells are unlocked with non-empty lists, the operation pops the node with the oldest timestamp. If one of the cells is unlocked and non-empty, the operation pops from that cell. If both cells are either locked or empty, the operation picks two new random cells and tries again.
    22695
    227 \subsection{Topological Work Stealing}
     96% \begin{figure}
     97%       \centering
     98%       \input{base.pstex_t}
     99%       \caption[Relaxed FIFO list]{Relaxed FIFO list \smallskip\newline List at the base of the scheduler: an array of strictly FIFO lists. The timestamp is in all nodes and cell arrays.}
     100%       \label{fig:base}
     101% \end{figure}
    228102
     103% \subsection{Finding threads}
     104% Once threads have been distributed onto multiple queues, identifying empty queues becomes a problem. Indeed, if the number of \glspl{thrd} does not far exceed the number of queues, it is probable that several of the cell 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. This scenario leads to performance problems since picks that do not yield a \gls{thrd} are not useful and do not necessarily help make more informed guesses.
    229105
     106% \begin{figure}
     107%       \centering
     108%       \input{empty.pstex_t}
     109%       \caption[``More empty'' Relaxed FIFO list]{``More empty'' Relaxed FIFO list \smallskip\newline Emptier state of the queue: the array contains many empty cells, that is strictly FIFO lists containing no elements.}
     110%       \label{fig:empty}
     111% \end{figure}
     112
     113% There are several solutions to this problem, but they ultimately all have to encode if a cell has an empty list. My results show 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:
     114
     115% \paragraph{Dense Information} Figure~\ref{fig:emptybit} shows a dense bitmask to identify the cell queues 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 searching 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 amount of ready-queue sharding 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 time increases. Finally, a dense bitmap, either single or multi-word, causes additional contention problems that 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 when the subsequent atomic check is done.
     116
     117% \begin{figure}
     118%       \centering
     119%       \vspace*{-5pt}
     120%       {\resizebox{0.75\textwidth}{!}{\input{emptybit.pstex_t}}}
     121%       \vspace*{-5pt}
     122%       \caption[Underloaded queue with bitmask]{Underloaded queue with bitmask indicating array cells with items.}
     123%       \label{fig:emptybit}
     124
     125%       \vspace*{10pt}
     126%       {\resizebox{0.75\textwidth}{!}{\input{emptytree.pstex_t}}}
     127%       \vspace*{-5pt}
     128%       \caption[Underloaded queue with binary search-tree]{Underloaded queue with binary search-tree indicating array cells with items.}
     129%       \label{fig:emptytree}
     130
     131%       \vspace*{10pt}
     132%       {\resizebox{0.95\textwidth}{!}{\input{emptytls.pstex_t}}}
     133%       \vspace*{-5pt}
     134%       \caption[Underloaded queue with per processor bitmask]{Underloaded queue with per processor bitmask indicating array cells with items.}
     135%       \label{fig:emptytls}
     136% \end{figure}
     137
     138% \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 significant contention on the nodes of the tree if the tree is shallow.
     139
     140% \paragraph{Local Information} Figure~\ref{fig:emptytls} shows an approach using dense information, similar to the bitmap, but each \gls{hthrd} keeps its own independent copy. 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 a few tries.
     141
     142% I built a prototype of these approaches and none of these techniques offer satisfying performance when few threads are present. All of these approach hit the same 2 problems. First, randomly picking sub-queues is very fast. That speed means any improvement to the hit rate can easily be countered by a slow-down in look-up speed, whether or not there are empty lists. Second, the array is already sharded to avoid contention bottlenecks, so any denser data structure tends to become a bottleneck. In all cases, these factors meant the best cases scenario, \ie many threads, would get worst throughput, and the worst-case scenario, few threads, would get a better hit rate, but an equivalent poor throughput. As a result I tried an entirely different approach.
     143
     144% \subsection{Dynamic Entropy}\cit{https://xkcd.com/2318/}
     145% In the worst-case scenario there are only few \glspl{thrd} ready to run, or more precisely given $P$ \glspl{proc}\footnote{For simplicity, this assumes there is a one-to-one match between \glspl{proc} and \glspl{hthrd}.}, $T$ \glspl{thrd} and $\epsilon$ a very small number, than the worst case scenario can be represented by $T = P + \epsilon$, with $\epsilon \ll P$. It is important to note in this case that fairness is effectively irrelevant. Indeed, this case is close to \emph{actually matching} the model of the ``Ideal multi-tasking CPU'' on page \pageref{q:LinuxCFS}. In this context, it is possible to use a purely internal-locality based approach and still meet the fairness requirements. This approach simply has each \gls{proc} running a single \gls{thrd} repeatedly. Or from the shared ready-queue viewpoint, each \gls{proc} pushes to a given sub-queue and then pops from the \emph{same} subqueue. The challenge is for the the scheduler to achieve good performance in both the $T = P + \epsilon$ case and the $T \gg P$ case, without affecting the fairness guarantees in the later.
     146
     147% To handle this case, I use a \glsxtrshort{prng}\todo{Fix missing long form} in a novel way. There exist \glsxtrshort{prng}s that are fast, compact and can be run forward \emph{and} backwards.  Linear congruential generators~\cite{wiki:lcg} are an example of \glsxtrshort{prng}s of such \glsxtrshort{prng}s. The novel approach is to use the ability to run backwards to ``replay'' the \glsxtrshort{prng}. The scheduler uses an exclusive \glsxtrshort{prng} instance per \gls{proc}, the random-number seed effectively starts an encoding that produces a list of all accessed subqueues, from latest to oldest. Replaying the \glsxtrshort{prng} to identify cells accessed recently and which probably have data still cached.
     148
     149% The algorithm works as follows:
     150% \begin{itemize}
     151%       \item Each \gls{proc} has two \glsxtrshort{prng} instances, $F$ and $B$.
     152%       \item Push and Pop operations occur as discussed in Section~\ref{sec:sharding} with the following exceptions:
     153%       \begin{itemize}
     154%               \item Push operations use $F$ going forward on each try and on success $F$ is copied into $B$.
     155%               \item Pop operations use $B$ going backwards on each try.
     156%       \end{itemize}
     157% \end{itemize}
     158
     159% The main benefit of this technique is that it basically respects the desired properties of Figure~\ref{fig:fair}. When looking for work, a \gls{proc} first looks at the last cell they pushed to, if any, and then move backwards through its accessed cells. As the \gls{proc} continues looking for work, $F$ moves backwards 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.
     160
     161% \section{Details}
  • doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex

    r92538ab r4559b34  
    33The first step of evaluation is always to test-out small controlled cases, to ensure that the basics are working properly.
    44This sections presents five different experimental setup, evaluating some of the basic features of \CFA's scheduler.
    5 
    6 \section{Benchmark Environment}
    7 All of these benchmarks are run on two distinct hardware environment, an AMD and an INTEL machine.
    8 
    9 \paragraph{AMD} The AMD machine is a server with two AMD EPYC 7662 CPUs and 256GB of DDR4 RAM.
    10 The server runs Ubuntu 20.04.2 LTS on top of Linux Kernel 5.8.0-55.
    11 These EPYCs have 64 cores per CPUs and 2 \glspl{hthrd} per core, for a total of 256 \glspl{hthrd}.
    12 The cpus each have 4 MB, 64 MB and 512 MB of L1, L2 and L3 caches respectively.
    13 Each L1 and L2 instance are only shared by \glspl{hthrd} on a given core, but each L3 instance is shared by 4 cores, therefore 8 \glspl{hthrd}.
    14 
    15 \paragraph{Intel} The Intel machine is a server with four Intel Xeon Platinum 8160 CPUs and 384GB of DDR4 RAM.
    16 The server runs Ubuntu 20.04.2 LTS on top of Linux Kernel 5.8.0-55.
    17 These Xeon Platinums have 24 cores per CPUs and 2 \glspl{hthrd} per core, for a total of 192 \glspl{hthrd}.
    18 The cpus each have 3 MB, 96 MB and 132 MB of L1, L2 and L3 caches respectively.
    19 Each L1 and L2 instance are only shared by \glspl{hthrd} on a given core, but each L3 instance is shared across the entire CPU, therefore 48 \glspl{hthrd}.
    20 
    21 This limited sharing of the last level cache on the AMD machine is markedly different than the Intel machine. Indeed, while on both architectures L2 cache misses that are served by L3 caches on a different cpu incurr a significant latency, on AMD it is also the case that cache misses served by a different L3 instance on the same cpu still incur high latency.
    22 
    235
    246\section{Cycling latency}
     
    4931\end{figure}
    5032
     33\todo{check term ``idle sleep handling''}
    5134To avoid this benchmark from being dominated by the idle sleep handling, the number of rings is kept at least as high as the number of \glspl{proc} available.
    5235Beyond this point, adding more rings serves to mitigate even more the idle sleep handling.
    53 This is to avoid the case where one of the \glspl{proc} runs out of work because of the variation on the number of ready \glspl{at} mentionned above.
     36This is to avoid the case where one of the worker \glspl{at} runs out of work because of the variation on the number of ready \glspl{at} mentionned above.
    5437
    5538The actual benchmark is more complicated to handle termination, but that simply requires using a binary semphore or a channel instead of raw \texttt{park}/\texttt{unpark} and carefully picking the order of the \texttt{P} and \texttt{V} with respect to the loop condition.
    5639
     40\todo{code, setup, results}
    5741\begin{lstlisting}
    5842        Thread.main() {
     
    6852\end{lstlisting}
    6953
    70 \begin{figure}
    71         \centering
    72         \input{result.cycle.jax.ops.pstex_t}
    73         \vspace*{-10pt}
    74         \label{fig:cycle:ns:jax}
    75 \end{figure}
    7654
    7755\section{Yield}
  • doc/theses/thierry_delisle_PhD/thesis/text/existing.tex

    r92538ab r4559b34  
    11\chapter{Previous Work}\label{existing}
    2 Scheduling is the process of assigning resources to incomming requests.
    3 A very common form of this is assigning available workers to work-requests.
    4 The need for scheduling is very common in Computer Science, \eg Operating Systems and Hypervisors schedule available CPUs, NICs schedule available bamdwith, but scheduling is also common in other fields.
    5 For example, in assmebly lines assigning parts in need of assembly to line workers is a form of scheduling.
    6 
    7 In all these cases, the choice of a scheduling algorithm generally depends first and formost on how much information is available to the scheduler.
    8 Workloads that are well-kown, consistent and homegenous can benefit from a scheduler that is optimized to use this information while ill-defined inconsistent heterogenous workloads will require general algorithms.
    9 A secondary aspect to that is how much information can be gathered versus how much information must be given as part of the input.
    10 There is therefore a spectrum of scheduling algorithms, going from static schedulers that are well informed from the start, to schedulers that gather most of the information needed, to schedulers that can only rely on very limitted information.
    11 Note that this description includes both infomation about each requests, \eg time to complete or resources needed, and information about the relationships between request, \eg whether or not some request must be completed before another request starts.
    12 
    13 Scheduling physical resources, for example in assembly lines, is generally amenable to using very well informed scheduling since information can be gathered much faster than the physical resources can be assigned and workloads are likely to stay stable for long periods of time.
    14 When a faster pace is needed and changes are much more frequent gathering information on workloads, up-front or live, can become much more limiting and more general schedulers are needed.
     2Scheduling is a topic with a very long history, predating its use in computer science. As such, early work in computed science was inspired from other fields and focused principally on solving scheduling upfront rather that as the system is running.
    153
    164\section{Naming Convention}
    17 Scheduling has been studied by various different communities concentrating on different incarnation of the same problems. As a result, their is no real naming convention for scheduling that is respected across these communities. For this document, I will use the term \newterm{\Gls{at}} to refer to the abstract objects being scheduled and the term \newterm{\Gls{proc}} to refer to the objects which will execute these \glspl{at}.
     5Scheduling has been studied by various different communities concentrating on different incarnation of the same problems. As a result, their is no real naming convention for scheduling that is respected across these communities. For this document, I will use the term \newterm{task} to refer to the abstract objects being scheduled and the term \newterm{worker} to refer to the objects which will execute these tasks.
    186
    197\section{Static Scheduling}
    20 Static schedulers require that \glspl{at} have their dependencies and costs explicitly and exhaustively specified prior schedule.
    21 The scheduler then processes this input ahead of time and producess a \newterm{schedule} to which the system can later adhere.
    22 This approach is generally popular in real-time systems since the need for strong guarantees justifies the cost of supplying this information.
     8Static schedulers require that programmers explicitly and exhaustively specify dependencies among tasks in order to schedule them. The scheduler then processes this input ahead of time and producess a \newterm{schedule} to which the system can later adhere. An example application for these schedulers
     9
    2310In general, static schedulers are less relavant to this project since they require input from the programmers that \CFA does not have as part of its concurrency semantic.
    24 Specifying this information explicitly can add a significant burden on the programmers and reduces flexibility, for this reason the \CFA scheduler does not require this information.
     11\todo{Rate-monotonic scheduling}
    2512
    2613
    2714\section{Dynamic Scheduling}
    28 It may be difficult to fulfill the requirements of static scheduler if dependencies are conditionnal. In this case, it may be preferable to detect dependencies at runtime. This detection effectively takes the form of adding one or more new \gls{at}(s) to the system as their dependencies are resolved. As well as potentially halting or suspending a \gls{at} that dynamically detect unfulfilled dependencies. Each \gls{at} has the responsability of adding the dependent \glspl{at} back in the system once completed. As a consequence, the scheduler may have an incomplete view of the system, seeing only \glspl{at} we no pending dependencies. Schedulers that support this detection at runtime are referred to as \newterm{Dynamic Schedulers}.
     15It may be difficult to fulfill the requirements of static scheduler if dependencies are be conditionnal. In this case, it may be preferable to detect dependencies at runtime. This detection effectively takes the form of halting or suspending a task with unfulfilled dependencies and adding one or more new task(s) to the system. The new task(s) have the responsability of adding the dependent task back in the system once completed. As a consequence, the scheduler may have an incomplete view of the system, seeing only tasks we no pending dependencies. Schedulers that support this detection at runtime are referred to as \newterm{Dynamic Schedulers}.
    2916
    3017\subsection{Explicitly Informed Dynamic Schedulers}
    31 While dynamic schedulers do not have access to an exhaustive list of dependencies for a \gls{at}, they may require to provide more or less information about each \gls{at}, including for example: expected duration, required ressources, relative importance, etc. The scheduler can then use this information to direct the scheduling decisions. \cit{Examples of schedulers with more information} Precisely providing this information can be difficult for programmers, especially \emph{predicted} behaviour, and the scheduler may need to support some amount of imprecision in the provided information. For example, specifying that a \glspl{at} takes approximately 5 seconds to complete, rather than exactly 5 seconds. User provided information can also become a significant burden depending how the effort to provide the information scales with the number of \glspl{at} and there complexity. For example, providing an exhaustive list of files read by 5 \glspl{at} is an easier requirement the providing an exhaustive list of memory addresses accessed by 10'000 distinct \glspl{at}.
     18While dynamic schedulers do not have access to an exhaustive list of dependencies for a task, they may require to provide more or less information about each task, including for example: expected duration, required ressources, relative importance, etc. The scheduler can then use this information to direct the scheduling decisions. \cit{Examples of schedulers with more information} Precisely providing this information can be difficult for programmers, especially \emph{predicted} behaviour, and the scheduler may need to support some amount of imprecision in the provided information. For example, specifying that a tasks takes approximately 5 seconds to complete, rather than exactly 5 seconds. User provided information can also become a significant burden depending how the effort to provide the information scales with the number of tasks and there complexity. For example, providing an exhaustive list of files read by 5 tasks is an easier requirement the providing an exhaustive list of memory addresses accessed by 10'000 distinct tasks.
    3219
    3320Since the goal of this thesis is to provide a scheduler as a replacement for \CFA's existing \emph{uninformed} scheduler, Explicitly Informed schedulers are less relevant to this project. Nevertheless, some strategies are worth mentionnding.
    3421
    3522\subsubsection{Prority Scheduling}
    36 A commonly used information that schedulers used to direct the algorithm is priorities. Each Task is given a priority and higher-priority \glspl{at} are preferred to lower-priority ones. The simplest priority scheduling algorithm is to simply require that every \gls{at} have a distinct pre-established priority and always run the available \gls{at} with the highest priority. Asking programmers to provide an exhaustive set of unique priorities can be prohibitive when the system has a large number of \glspl{at}. It can therefore be diserable for schedulers to support \glspl{at} with identical priorities and/or automatically setting and adjusting priorites for \glspl{at}. The most common operating some variation on priorities with overlaps and dynamic priority adjustments. For example, Microsoft Windows uses a pair of priorities
    37 \cit{https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities,https://docs.microsoft.com/en-us/windows/win32/taskschd/taskschedulerschema-priority-settingstype-element}, one specified by users out of ten possible options and one adjusted by the system.
     23A commonly used information that schedulers used to direct the algorithm is priorities. Each Task is given a priority and higher-priority tasks are preferred to lower-priority ones. The simplest priority scheduling algorithm is to simply require that every task have a distinct pre-established priority and always run the available task with the highest priority. Asking programmers to provide an exhaustive set of unique priorities can be prohibitive when the system has a large number of tasks. It can therefore be diserable for schedulers to support tasks with identical priorities and/or automatically setting and adjusting priorites for tasks.
    3824
    3925\subsection{Uninformed and Self-Informed Dynamic Schedulers}
    40 Several scheduling algorithms do not require programmers to provide additionnal information on each \gls{at}, and instead make scheduling decisions based solely on internal state and/or information implicitly gathered by the scheduler.
     26Several scheduling algorithms do not require programmers to provide additionnal information on each task, and instead make scheduling decisions based solely on internal state and/or information implicitly gathered by the scheduler.
    4127
    4228
    4329\subsubsection{Feedback Scheduling}
    44 As mentionned, Schedulers may also gather information about each \glspl{at} to direct their decisions. This design effectively moves the scheduler to some extent into the realm of \newterm{Control Theory}\cite{wiki:controltheory}. This gathering does not generally involve programmers and as such does not increase programmer burden the same way explicitly provided information may. However, some feedback schedulers do offer the option to programmers to offer additionnal information on certain \glspl{at}, in order to direct scheduling decision. The important distinction being whether or not the scheduler can function without this additionnal information.
     30As mentionned, Schedulers may also gather information about each tasks to direct their decisions. This design effectively moves the scheduler to some extent into the realm of \newterm{Control Theory}\cite{wiki:controltheory}. This gathering does not generally involve programmers and as such does not increase programmer burden the same way explicitly provided information may. However, some feedback schedulers do offer the option to programmers to offer additionnal information on certain tasks, in order to direct scheduling decision. The important distinction being whether or not the scheduler can function without this additionnal information.
     31
     32Feedback scheduler
    4533
    4634
    4735\section{Work Stealing}\label{existing:workstealing}
    48 One of the most popular scheduling algorithm in practice (see~\ref{existing:prod}) is work-stealing. This idea, introduce by \cite{DBLP:conf/fpca/BurtonS81}, effectively has each worker work on its local \glspl{at} first, but allows the possibility for other workers to steal local \glspl{at} if they run out of \glspl{at}. \cite{DBLP:conf/focs/Blumofe94} introduced the more familiar incarnation of this, where each workers has queue of \glspl{at} to accomplish and workers without \glspl{at} steal \glspl{at} from random workers. (The Burton and Sleep algorithm had trees of \glspl{at} and stole only among neighbours). Blumofe and Leiserson also prove worst case space and time requirements for well-structured computations.
     36One of the most popular scheduling algorithm in practice (see~\ref{existing:prod}) is work-stealing. This idea, introduce by \cite{DBLP:conf/fpca/BurtonS81}, effectively has each worker work on its local tasks first, but allows the possibility for other workers to steal local tasks if they run out of tasks. \cite{DBLP:conf/focs/Blumofe94} introduced the more familiar incarnation of this, where each workers has queue of tasks to accomplish and workers without tasks steal tasks from random workers. (The Burton and Sleep algorithm had trees of tasks and stole only among neighbours). Blumofe and Leiserson also prove worst case space and time requirements for well-structured computations.
    4937
    5038Many variations of this algorithm have been proposed over the years\cite{DBLP:journals/ijpp/YangH18}, both optmizations of existing implementations and approaches that account for new metrics.
     
    5240\paragraph{Granularity} A significant portion of early Work Stealing research was concentrating on \newterm{Implicit Parellelism}\cite{wiki:implicitpar}. Since the system was responsible to split the work, granularity is a challenge that cannot be left to the programmers (as opposed to \newterm{Explicit Parellelism}\cite{wiki:explicitpar} where the burden can be left to programmers). In general, fine granularity is better for load balancing and coarse granularity reduces communication overhead. The best performance generally means finding a middle ground between the two. Several methods can be employed, but I believe these are less relevant for threads, which are generally explicit and more coarse grained.
    5341
    54 \paragraph{Task Placement} Since modern computers rely heavily on cache hierarchies\cit{Do I need a citation for this}, migrating \glspl{at} from one core to another can be .  \cite{DBLP:journals/tpds/SquillanteL93}
     42\paragraph{Task Placement} Since modern computers rely heavily on cache hierarchies\cit{Do I need a citation for this}, migrating tasks from one core to another can be .  \cite{DBLP:journals/tpds/SquillanteL93}
    5543
    5644\todo{The survey is not great on this subject}
     
    5947
    6048\subsection{Theoretical Results}
    61 There is also a large body of research on the theoretical aspects of work stealing. These evaluate, for example, the cost of migration\cite{DBLP:conf/sigmetrics/SquillanteN91,DBLP:journals/pe/EagerLZ86}, how affinity affects performance\cite{DBLP:journals/tpds/SquillanteL93,DBLP:journals/mst/AcarBB02,DBLP:journals/ipl/SuksompongLS16} and theoretical models for heterogenous systems\cite{DBLP:journals/jpdc/MirchandaneyTS90,DBLP:journals/mst/BenderR02,DBLP:conf/sigmetrics/GastG10}. \cite{DBLP:journals/jacm/BlellochGM99} examine the space bounds of Work Stealing and \cite{DBLP:journals/siamcomp/BerenbrinkFG03} show that for underloaded systems, the scheduler will complete computations in finite time, \ie is \newterm{stable}. Others show that Work-Stealing is applicable to various scheduling contexts\cite{DBLP:journals/mst/AroraBP01,DBLP:journals/anor/TchiboukdjianGT13,DBLP:conf/isaac/TchiboukdjianGTRB10,DBLP:conf/ppopp/AgrawalLS10,DBLP:conf/spaa/AgrawalFLSSU14}. \cite{DBLP:conf/ipps/ColeR13} also studied how Randomized Work Stealing affects false sharing among \glspl{at}.
     49There is also a large body of research on the theoretical aspects of work stealing. These evaluate, for example, the cost of migration\cite{DBLP:conf/sigmetrics/SquillanteN91,DBLP:journals/pe/EagerLZ86}, how affinity affects performance\cite{DBLP:journals/tpds/SquillanteL93,DBLP:journals/mst/AcarBB02,DBLP:journals/ipl/SuksompongLS16} and theoretical models for heterogenous systems\cite{DBLP:journals/jpdc/MirchandaneyTS90,DBLP:journals/mst/BenderR02,DBLP:conf/sigmetrics/GastG10}. \cite{DBLP:journals/jacm/BlellochGM99} examine the space bounds of Work Stealing and \cite{DBLP:journals/siamcomp/BerenbrinkFG03} show that for underloaded systems, the scheduler will complete computations in finite time, \ie is \newterm{stable}. Others show that Work-Stealing is applicable to various scheduling contexts\cite{DBLP:journals/mst/AroraBP01,DBLP:journals/anor/TchiboukdjianGT13,DBLP:conf/isaac/TchiboukdjianGTRB10,DBLP:conf/ppopp/AgrawalLS10,DBLP:conf/spaa/AgrawalFLSSU14}. \cite{DBLP:conf/ipps/ColeR13} also studied how Randomized Work Stealing affects false sharing among tasks.
    6250
    6351However, as \cite{DBLP:journals/ijpp/YangH18} highlights, it is worth mentionning that this theoretical research has mainly focused on ``fully-strict'' computations, \ie workloads that can be fully represented with a Direct Acyclic Graph. It is unclear how well these distributions represent workloads in real world scenarios.
    6452
    6553\section{Preemption}
    66 One last aspect of scheduling worth mentionning is preemption since many schedulers rely on it for some of their guarantees. Preemption is the idea of interrupting \glspl{at} that have been running for too long, effectively injecting suspend points in the applications. There are multiple techniques to achieve this but they all aim to have the effect of guaranteeing that suspend points in a \gls{at} are never further apart than some fixed duration. While this helps schedulers guarantee that no \glspl{at} will unfairly monopolize a worker, preemption can effectively added to any scheduler. Therefore, the only interesting aspect of preemption for the design of scheduling is whether or not to require it.
     54One last aspect of scheduling worth mentionning is preemption since many schedulers rely on it for some of their guarantees. Preemption is the idea of interrupting tasks that have been running for too long, effectively injecting suspend points in the applications. There are multiple techniques to achieve this but they all aim to have the effect of guaranteeing that suspend points in a task are never further apart than some fixed duration. While this helps schedulers guarantee that no tasks will unfairly monopolize a worker, preemption can effectively added to any scheduler. Therefore, the only interesting aspect of preemption for the design of scheduling is whether or not to require it.
    6755
    6856\section{Schedulers in Production}\label{existing:prod}
     
    7058
    7159\subsection{Operating System Schedulers}
    72 Operating System Schedulers tend to be fairly complex schedulers, they generally support some amount of real-time, aim to balance interactive and non-interactive \glspl{at} and support for multiple users sharing hardware without requiring these users to cooperate. Here are more details on a few schedulers used in the common operating systems: Linux, FreeBsd, Microsoft Windows and Apple's OS X. The information is less complete for operating systems behind closed source.
     60Operating System Schedulers tend to be fairly complex schedulers, they generally support some amount of real-time, aim to balance interactive and non-interactive tasks and support for multiple users sharing hardware without requiring these users to cooperate. Here are more details on a few schedulers used in the common operating systems: Linux, FreeBsd, Microsoft Windows and Apple's OS X. The information is less complete for operating systems behind closed source.
    7361
    7462\paragraph{Linux's CFS}
    75 The default scheduler used by Linux (the Completely Fair Scheduler)\cite{MAN:linux/cfs,MAN:linux/cfs2} is a feedback scheduler based on CPU time. For each processor, it constructs a Red-Black tree of \glspl{at} waiting to run, ordering them by amount of CPU time spent. The scheduler schedules the \gls{at} that has spent the least CPU time. It also supports the concept of \newterm{Nice values}, which are effectively multiplicative factors on the CPU time spent. The ordering of \glspl{at} is also impacted by a group based notion of fairness, where \glspl{at} belonging to groups having spent less CPU time are preferred to \glspl{at} beloning to groups having spent more CPU time. Linux achieves load-balancing by regularly monitoring the system state\cite{MAN:linux/cfs/balancing} and using some heuristic on the load (currently CPU time spent in the last millisecond plus decayed version of the previous time slots\cite{MAN:linux/cfs/pelt}.).
     63The default scheduler used by Linux (the Completely Fair Scheduler)\cite{MAN:linux/cfs,MAN:linux/cfs2} is a feedback scheduler based on CPU time. For each processor, it constructs a Red-Black tree of tasks waiting to run, ordering them by amount of CPU time spent. The scheduler schedules the task that has spent the least CPU time. It also supports the concept of \newterm{Nice values}, which are effectively multiplicative factors on the CPU time spent. The ordering of tasks is also impacted by a group based notion of fairness, where tasks belonging to groups having spent less CPU time are preferred to tasks beloning to groups having spent more CPU time. Linux achieves load-balancing by regularly monitoring the system state\cite{MAN:linux/cfs/balancing} and using some heuristic on the load (currently CPU time spent in the last millisecond plus decayed version of the previous time slots\cite{MAN:linux/cfs/pelt}.).
    7664
    77 \cite{DBLP:conf/eurosys/LoziLFGQF16} shows that Linux's CFS also does work-stealing to balance the workload of each processors, but the paper argues this aspect can be improved significantly. The issues highlighted sem to stem from Linux's need to support fairness across \glspl{at} \emph{and} across users\footnote{Enforcing fairness across users means, for example, that given two users: one with a single \gls{at} and the other with one thousand \glspl{at}, the user with a single \gls{at} does not receive one one thousandth of the CPU time.}, increasing the complexity.
     65\cite{DBLP:conf/eurosys/LoziLFGQF16} shows that Linux's CFS also does work-stealing to balance the workload of each processors, but the paper argues this aspect can be improved significantly. The issues highlighted sem to stem from Linux's need to support fairness across tasks \emph{and} across users\footnote{Enforcing fairness across users means, for example, that given two users: one with a single task and the other with one thousand tasks, the user with a single task does not receive one one thousandth of the CPU time.}, increasing the complexity.
    7866
    79 Linux also offers a FIFO scheduler, a real-time schedulerwhich runs the highest-priority \gls{at}, and a round-robin scheduler, which is an extension of the fifo-scheduler that adds fixed time slices. \cite{MAN:linux/sched}
     67Linux also offers a FIFO scheduler, a real-time schedulerwhich runs the highest-priority task, and a round-robin scheduler, which is an extension of the fifo-scheduler that adds fixed time slices. \cite{MAN:linux/sched}
    8068
    8169\paragraph{FreeBSD}
     
    8371
    8472\paragraph{Windows(OS)}
    85 Microsoft's Operating System's Scheduler\cite{MAN:windows/scheduler} is a feedback scheduler with priorities. It supports 32 levels of priorities, some of which are reserved for real-time and prviliged applications. It schedules \glspl{at} based on the highest priorities (lowest number) and how much cpu time each \glspl{at} have used. The scheduler may also temporarily adjust priorities after certain effects like the completion of I/O requests.
     73Microsoft's Operating System's Scheduler\cite{MAN:windows/scheduler} is a feedback scheduler with priorities. It supports 32 levels of priorities, some of which are reserved for real-time and prviliged applications. It schedules tasks based on the highest priorities (lowest number) and how much cpu time each tasks have used. The scheduler may also temporarily adjust priorities after certain effects like the completion of I/O requests.
    8674
    8775\todo{load balancing}
     
    10088
    10189\subsection{User-Level Schedulers}
    102 By comparison, user level schedulers tend to be simpler, gathering fewer metrics and avoid complex notions of fairness. Part of the simplicity is due to the fact that all \glspl{at} have the same user, and therefore cooperation is both feasible and probable.
     90By comparison, user level schedulers tend to be simpler, gathering fewer metrics and avoid complex notions of fairness. Part of the simplicity is due to the fact that all tasks have the same user, and therefore cooperation is both feasible and probable.
    10391\paragraph{Go}
    10492Go's scheduler uses a Randomized Work Stealing algorithm that has a global runqueue(\emph{GRQ}) and each processor(\emph{P}) has both a fixed-size runqueue(\emph{LRQ}) and a high-priority next ``chair'' holding a single element.\cite{GITHUB:go,YTUBE:go} Preemption is present, but only at function call boundaries.
     
    117105
    118106\paragraph{Intel\textregistered ~Threading Building Blocks}
    119 \newterm{Thread Building Blocks}(TBB) is Intel's task parellelism\cite{wiki:taskparallel} framework. It runs \newterm{jobs}, uninterruptable \glspl{at}, schedulable objects that must always run to completion, on a pool of worker threads. TBB's scheduler is a variation of Randomized Work Stealing that also supports higher-priority graph-like dependencies\cite{MAN:tbb/scheduler}. It schedules \glspl{at} as follows (where \textit{t} is the last \gls{at} completed):
     107\newterm{Thread Building Blocks}(TBB) is Intel's task parellelism\cite{wiki:taskparallel} framework. It runs tasks or \newterm{jobs}, schedulable objects that must always run to completion, on a pool of worker threads. TBB's scheduler is a variation of Randomized Work Stealing that also supports higher-priority graph-like dependencies\cite{MAN:tbb/scheduler}. It schedules tasks as follows (where \textit{t} is the last task completed):
    120108\begin{displayquote}
    121109        \begin{enumerate}
     
    137125
    138126\paragraph{Grand Central Dispatch}
    139 This is an API produce by Apple\cit{Official GCD source} that offers task parellelism\cite{wiki:taskparallel}. Its distinctive aspect is that it uses multiple ``Dispatch Queues'', some of which are created by programmers. These queues each have their own local ordering guarantees, \eg \glspl{at} on queue $A$ are executed in \emph{FIFO} order.
     127This is an API produce by Apple\cit{Official GCD source} that offers task parellelism\cite{wiki:taskparallel}. Its distinctive aspect is that it uses multiple ``Dispatch Queues'', some of which are created by programmers. These queues each have their own local ordering guarantees, \eg tasks on queue $A$ are executed in \emph{FIFO} order.
    140128
    141129\todo{load balancing and scheduling}
  • doc/theses/thierry_delisle_PhD/thesis/text/io.tex

    r92538ab r4559b34  
    11\chapter{User Level \io}
    22As mentioned in Section~\ref{prev:io}, User-Level \io requires multiplexing the \io operations of many \glspl{thrd} onto fewer \glspl{proc} using asynchronous \io operations.
    3 Different operating systems offer various forms of asynchronous operations and, as mentioned in Chapter~\ref{intro}, this work is exclusively focused on the Linux operating-system.
     3Different operating systems offer various forms of asynchronous operations and as mentioned in Chapter~\ref{intro}, this work is exclusively focused on the Linux operating-system.
    44
    55\section{Kernel Interface}
     
    173173The consequence is that the amount of parallelism used to prepare submissions for the next system call is limited.
    174174Beyond this limit, the length of the system call is the throughput limiting factor.
    175 I concluded from early experiments that preparing submissions seems to take at most as long as the system call itself, which means that with a single @io_uring@ instance, there is no benefit in terms of \io throughput to having more than two \glspl{hthrd}.
     175I concluded from early experiments that preparing submissions seems to take about as long as the system call itself, which means that with a single @io_uring@ instance, there is no benefit in terms of \io throughput to having more than two \glspl{hthrd}.
    176176Therefore the design of the submission engine must manage multiple instances of @io_uring@ running in parallel, effectively sharding @io_uring@ instances.
    177177Similarly to scheduling, this sharding can be done privately, \ie, one instance per \glspl{proc}, in decoupled pools, \ie, a pool of \glspl{proc} use a pool of @io_uring@ instances without one-to-one coupling between any given instance and any given \gls{proc}, or some mix of the two.
    178178Since completions are sent to the instance where requests were submitted, all instances with pending operations must be polled continously
    179179\footnote{As will be described in Chapter~\ref{practice}, this does not translate into constant cpu usage.}.
    180 Note that once an operation completes, there is nothing that ties it to the @io_uring@ instance that handled it.
    181 There is nothing preventing a new operation with, for example, the same file descriptors to a different @io_uring@ instance.
    182180
    183181A complicating aspect of submission is @io_uring@'s support for chains of operations, where the completion of an operation triggers the submission of the next operation on the link.
     
    200198The only added complexity is that the number of SQEs is fixed, which means allocation can fail.
    201199
    202 Allocation failures need to be pushed up to a routing algorithm: \glspl{thrd} attempting \io operations must not be directed to @io_uring@ instances without sufficient SQEs available.
     200Allocation failures need to be pushed up to the routing algorithm: \glspl{thrd} attempting \io operations must not be directed to @io_uring@ instances without sufficient SQEs available.
    203201Furthermore, the routing algorithm should block operations up-front if none of the instances have available SQEs.
    204202
     
    214212
    215213In the case of designating a \gls{thrd}, ideally, when multiple \glspl{thrd} attempt to submit operations to the same @io_uring@ instance, all requests would be batched together and one of the \glspl{thrd} would do the system call on behalf of the others, referred to as the \newterm{submitter}.
    216 In practice however, it is important that the \io requests are not left pending indefinitely and as such, it may be required to have a ``next submitter'' that guarentees everything that is missed by the current submitter is seen by the next one.
     214In practice however, it is important that the \io requests are not left pending indefinitely and as such, it may be required to have a current submitter and a next submitter.
    217215Indeed, as long as there is a ``next'' submitter, \glspl{thrd} submitting new \io requests can move on, knowing that some future system call will include their request.
    218216Once the system call is done, the submitter must also free SQEs so that the allocator can reused them.
     
    223221If the submission side does not designate submitters, polling can also submit all SQEs as it is polling events.
    224222A simple approach to polling is to allocate a \gls{thrd} per @io_uring@ instance and simply let the poller \glspl{thrd} poll their respective instances when scheduled.
     223This design is especially convenient for reasons explained in Chapter~\ref{practice}.
    225224
    226225With this pool of instances approach, the big advantage is that it is fairly flexible.
    227226It does not impose restrictions on what \glspl{thrd} submitting \io operations can and cannot do between allocations and submissions.
    228 It also can gracefully handle running out of ressources, SQEs or the kernel returning @EBUSY@.
     227It also can gracefully handles running out of ressources, SQEs or the kernel returning @EBUSY@.
    229228The down side to this is that many of the steps used for submitting need complex synchronization to work properly.
    230229The routing and allocation algorithm needs to keep track of which ring instances have available SQEs, block incoming requests if no instance is available, prevent barging if \glspl{thrd} are already queued up waiting for SQEs and handle SQEs being freed.
    231230The submission side needs to safely append SQEs to the ring buffer, correctly handle chains, make sure no SQE is dropped or left pending forever, notify the allocation side when SQEs can be reused and handle the kernel returning @EBUSY@.
    232 All this synchronization may have a significant cost and, compared to the next approach presented, this synchronization is entirely overhead.
     231All this synchronization may have a significant cost and, compare to the next approach presented, this synchronization is entirely overhead.
    233232
    234233\subsubsection{Private Instances}
    235234Another approach is to simply create one ring instance per \gls{proc}.
    236 This alleviates the need for synchronization on the submissions, requiring only that \glspl{thrd} are not interrupted in between two submission steps.
     235This alleviate the need for synchronization on the submissions, requiring only that \glspl{thrd} are not interrupted in between two submission steps.
    237236This is effectively the same requirement as using @thread_local@ variables.
    238237Since SQEs that are allocated must be submitted to the same ring, on the same \gls{proc}, this effectively forces the application to submit SQEs in allocation order
     
    241240To remove this requirement, a \gls{thrd} would need the ability to ``yield to a specific \gls{proc}'', \ie, park with the promise that it will be run next on a specific \gls{proc}, the \gls{proc} attached to the correct ring.}
    242241, greatly simplifying both allocation and submission.
    243 In this design, allocation and submission form a partitionned ring buffer as shown in Figure~\ref{fig:pring}.
     242In this design, allocation and submission form a ring partitionned ring buffer as shown in Figure~\ref{fig:pring}.
    244243Once added to the ring buffer, the attached \gls{proc} has a significant amount of flexibility with regards to when to do the system call.
    245 Possible options are: when the \gls{proc} runs out of \glspl{thrd} to run, after running a given number of \glspl{thrd}, etc.
     244Possible options are: when the \gls{proc} runs out of \glspl{thrd} to run, after running a given number of threads \glspl{thrd}, etc.
    246245
    247246\begin{figure}
     
    330329\paragraph{Pending Allocations} can be more complicated to handle.
    331330If the arbiter has available instances, the arbiter can attempt to directly hand over the instance and satisfy the request.
    332 Otherwise it must hold onto the list of threads until SQEs are made available again.
    333 This handling becomes that much more complex if pending allocation require more than one SQE, since the arbiter must make a decision between statisfying requests in FIFO ordering or satisfy requests for fewer SQEs first.
    334 
    335 While this arbiter has the potential to solve many of the problems mentionned in above, it also introduces a significant amount of complexity.
    336 Tracking which processors are borrowing which instances and which instances have SQEs available ends-up adding a significant synchronization prelude to any I/O operation.
    337 Any submission must start with a handshake that pins the currently borrowed instance, if available.
    338 An attempt to allocate is then made, but the arbiter can concurrently be attempting to allocate from the same instance from a different \gls{hthrd}.
    339 Once the allocation is completed, the submission must still check that the instance is still burrowed before attempt to flush.
    340 These extra synchronization steps end-up having a similar cost to the multiple shared instances approach.
    341 Furthermore, if the number of instances does not match the number of processors actively submitting I/O, the system can fall into a state where instances are constantly being revoked and end-up cycling the processors, which leads to significant cache deterioration.
    342 Because of these reasons, this approach, which sounds promising on paper, does not improve on the private instance approach in practice.
    343 
    344 \subsubsection{Private Instances V2}
    345 
     331Otherwise
    346332
    347333
  • doc/theses/thierry_delisle_PhD/thesis/thesis.tex

    r92538ab r4559b34  
    202202
    203203\newcommand\io{\glsxtrshort{io}\xspace}%
    204 \newcommand\at{\gls{at}\xspace}%
    205 \newcommand\ats{\glspl{at}\xspace}%
    206 \newcommand\proc{\gls{proc}\xspace}%
    207 \newcommand\procs{\glspl{proc}\xspace}%
    208204
    209205%======================================================================
  • doc/user/user.tex

    r92538ab r4559b34  
    1111%% Created On       : Wed Apr  6 14:53:29 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Mon Feb 14 17:20:39 2022
    14 %% Update Count     : 5382
     13%% Last Modified On : Sun Oct 10 12:45:00 2021
     14%% Update Count     : 5095
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
    1717% requires tex packages: texlive-base texlive-latex-base tex-common texlive-humanities texlive-latex-extra texlive-fonts-recommended
    1818
    19 \documentclass[twoside]{article}
     19\documentclass[twoside,11pt]{article}
    2020
    2121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     
    4040% blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_
    4141% green highlighting ¢...¢ (cent symbol) emacs: C-q M-"
    42 % LaTex escape ...§ (section symbol) emacs: C-q M-'
     42% LaTex escape §...§ (section symbol) emacs: C-q M-'
    4343% keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^
    4444% math escape $...$ (dollar symbol)
     
    8585\newcommand{\B}[1]{{\Textbf[blue]{#1}}}
    8686\newcommand{\G}[1]{{\Textbf[OliveGreen]{#1}}}
    87 \newcommand{\Sp}{\R{\textvisiblespace}}
    8887\newcommand{\KWC}{K-W C\xspace}
    8988
     
    157156One of the main design philosophies of \CFA is to ``\Index{describe not prescribe}'', which means \CFA tries to provide a pathway from low-level C programming to high-level \CFA programming, but it does not force programmers to ``do the right thing''.
    158157Programmers can cautiously add \CFA extensions to their C programs in any order and at any time to incrementally move towards safer, higher-level programming.
    159 A programmer is always free to reach back to C from \CFA, for any reason, and in many cases, new \CFA features can be locally switched back to their C counterpart.
    160 There is no notion or requirement for \emph{rewriting} a legacy C program to \CFA;
     158A programmer is always free to reach back to C from \CFA, for any reason, and in many cases, new \CFA features can be locally switched back to there C counterpart.
     159There is no notion or requirement for \emph{rewriting} a legacy C program in \CFA;
    161160instead, a programmer evolves a legacy program into \CFA by incrementally incorporating \CFA features.
    162161As well, new programs can be written in \CFA using a combination of C and \CFA features.
     
    164163
    165164\Index*[C++]{\CC{}}~\cite{c++:v1} had a similar goal 30 years ago, allowing object-oriented programming to be incrementally added to C.
    166 However, \CC currently has the disadvantages of a strong object-oriented bias, multiple legacy design-choices that are difficult to update, and active divergence of the language model from C, requiring significant effort and training to incrementally add \CC to a C-based project.
     165However, \CC currently has the disadvantages of a strong object-oriented bias, multiple legacy design-choices that cannot be updated, and active divergence of the language model from C, requiring significant effort and training to incrementally add \CC to a C-based project.
    167166In contrast, \CFA has 30 years of hindsight and a clean starting point.
    168167
    169168Like \Index*[C++]{\CC{}}, there may be both old and new ways to achieve the same effect.
    170169For example, the following programs compare the C, \CFA, and \CC I/O mechanisms, where the programs output the same result.
    171 \begin{flushleft}
     170\begin{center}
    172171\begin{tabular}{@{}l@{\hspace{1em}}l@{\hspace{1em}}l@{}}
    173 \multicolumn{1}{@{}c@{\hspace{1em}}}{\textbf{C}}        & \multicolumn{1}{c}{\textbf{\CFA}}     & \multicolumn{1}{c@{}}{\textbf{\CC}}   \\
    174 \begin{cfa}[tabsize=3]
     172\multicolumn{1}{c@{\hspace{1em}}}{\textbf{C}}   & \multicolumn{1}{c}{\textbf{\CFA}}     & \multicolumn{1}{c}{\textbf{\CC}}      \\
     173\begin{cfa}
    175174#include <stdio.h>$\indexc{stdio.h}$
    176175
     
    181180\end{cfa}
    182181&
    183 \begin{cfa}[tabsize=3]
     182\begin{cfa}
    184183#include <fstream>$\indexc{fstream}$
    185184
     
    190189\end{cfa}
    191190&
    192 \begin{cfa}[tabsize=3]
     191\begin{cfa}
    193192#include <iostream>$\indexc{iostream}$
    194193using namespace std;
    195194int main() {
    196195        int x = 0, y = 1, z = 2;
    197         ®cout << x << ' ' << y << ' ' << z << endl;®
     196        ®cout<<x<<" "<<y<<" "<<z<<endl;®
    198197}
    199198\end{cfa}
    200199\end{tabular}
    201 \end{flushleft}
     200\end{center}
    202201While \CFA I/O \see{\VRef{s:StreamIOLibrary}} looks similar to \Index*[C++]{\CC{}}, there are important differences, such as automatic spacing between variables and an implicit newline at the end of the expression list, similar to \Index*{Python}~\cite{Python}.
    203202
     
    239238however, it largely extended the C language, and did not address many of C's existing problems.\footnote{%
    240239Two important existing problems addressed were changing the type of character literals from ©int© to ©char© and enumerator from ©int© to the type of its enumerators.}
    241 \Index*{Fortran}~\cite{Fortran08}, \Index*{Cobol}~\cite{Cobol14}, and \Index*{Ada}~\cite{Ada12} are examples of programming languages that took an evolutionary approach, where modern language-features (\eg objects, concurrency) are added and problems fixed within the framework of the existing language.
     240\Index*{Fortran}~\cite{Fortran08}, \Index*{Ada}~\cite{Ada12}, and \Index*{Cobol}~\cite{Cobol14} are examples of programming languages that took an evolutionary approach, where modern language-features (\eg objects, concurrency) are added and problems fixed within the framework of the existing language.
    242241\Index*{Java}~\cite{Java8}, \Index*{Go}~\cite{Go}, \Index*{Rust}~\cite{Rust} and \Index*{D}~\cite{D} are examples of the revolutionary approach for modernizing C/\CC, resulting in a new language rather than an extension of the descendent.
    243242These languages have different syntax and semantics from C, do not interoperate directly with C, and are not systems languages because of restrictive memory-management or garbage collection.
     
    334333long double _Complex ®abs®( long double _Complex );
    335334\end{cfa}
    336 The problem is a \Index{name clash} between the C name ©abs© and the \CFA names ©abs©, resulting in two name linkages\index{C linkage}: ©extern "C"© and ©extern "Cforall"© (default).
     335The problem is \Index{name clash} between the C name ©abs© and the \CFA names ©abs©, resulting in two name linkages\index{C linkage}: ©extern "C"© and ©extern "Cforall"© (default).
    337336Overloaded names must use \newterm{name mangling}\index{mangling!name} to create unique names that are different from unmangled C names.
    338337Hence, there is the same need as in \CC to know if a name is a C or \CFA name, so it can be correctly formed.
     
    378377The program is linked with the debugging version of the runtime system.
    379378The debug version performs runtime checks to aid the debugging phase of a \CFA program, but can substantially slow program execution.
    380 The runtime checks should only be removed after a program is completely debugged.
     379The runtime checks should only be removed after the program is completely debugged.
    381380\textbf{This option is the default.}
    382381
     
    453452cfa $test$.cfa -XCFA -P -XCFA parse -XCFA -n # show program parse without prelude
    454453\end{lstlisting}
    455 Alternatively, multiple flages can be specified separated with commas and \emph{without} spaces.
    456 \begin{lstlisting}[language=sh,{moredelim=**[is][\protect\color{red}]{®}{®}}]
    457 cfa $test$.cfa -XCFA®,®-Pparse®,®-n # show program parse without prelude
    458 \end{lstlisting}
    459454\begin{description}[topsep=5pt,itemsep=0pt,parsep=0pt]
    460455\item
     
    538533double ®``®forall = 3.5;
    539534\end{cfa}
    540 Existing C programs with keyword clashes can be converted by prefixing the keyword identifiers with double backquotes, and eventually the identifier name can be changed to a non-keyword name.
     535
     536Existing C programs with keyword clashes can be converted by enclosing keyword identifiers in backquotes, and eventually the identifier name can be changed to a non-keyword name.
    541537\VRef[Figure]{f:HeaderFileInterposition} shows how clashes in existing C header-files \see{\VRef{s:StandardHeaders}} can be handled using preprocessor \newterm{interposition}: ©#include_next© and ©-I filename©.
    542538Several common C header-files with keyword clashes are fixed in the standard \CFA header-library, so there is a seamless programming-experience.
     
    631627\subsection{\texorpdfstring{\LstKeywordStyle{if} / \LstKeywordStyle{while} Statement}{if / while Statement}}
    632628
    633 The \Indexc{if}/\Indexc{while} expression allows declarations, similar to \Indexc{for} declaration expression.\footnote{
    634 Declarations in the \Indexc{do}-©while© condition are not useful because they appear after the loop body.}
     629The ©if©/©while© expression allows declarations, similar to ©for© declaration expression.\footnote{
     630Declarations in the ©do©-©while© condition are not useful because they appear after the loop body.}
    635631\begin{cfa}
    636632if ( ®int x = f()® ) ... $\C{// x != 0}$
     
    644640while ( ®struct S { int i; } x = { f() }; x.i < 4® ) ... $\C{// relational expression}$
    645641\end{cfa}
    646 Unless a relational expression is specified, each variable is compared not equal to 0, which is the standard semantics for the ©if©/©while© expression, and the results are combined using the logical \Indexc{&&} operator.
    647 The scope of the declaration(s) is local to the ©if©/©while© statement, \ie in both \emph{then} and \emph{else} clauses for ©if©, and loop body for ©while©.
     642Unless a relational expression is specified, each variable is compared not equal to 0, which is the standard semantics for the ©if©/©while© expression, and the results are combined using the logical ©&&© operator.
     643The scope of the declaration(s) is local to the ©if© statement but exist within both the \emph{then} and \emph{else} clauses.
    648644\CC only provides a single declaration always compared ©!=© to 0.
    649645
     
    653649\label{s:caseClause}
    654650
    655 C restricts the \Indexc{case} clause of a \Indexc{switch} statement to a single value.
     651C restricts the ©case© clause of a ©switch© statement to a single value.
    656652For multiple ©case© clauses associated with the same statement, it is necessary to have multiple ©case© clauses rather than multiple values.
    657 Requiring a ©case© clause for each value is not in the spirit of brevity normally associated with C.
    658 Therefore, the ©case© clause is extended with a list of values.
     653Requiring a ©case© clause for each value does not seem to be in the spirit of brevity normally associated with C.
     654Therefore, the ©case© clause is extended with a list of values, as in:
    659655\begin{cquote}
    660656\begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{2em}}l@{}}
     
    707703\subsection{\texorpdfstring{\LstKeywordStyle{switch} Statement}{switch Statement}}
    708704
    709 C allows a number of questionable forms for the \Indexc{switch} statement:
     705C allows a number of questionable forms for the ©switch© statement:
    710706\begin{enumerate}
    711707\item
    712 By default, the end of a \Indexc{case} clause\footnote{
     708By default, the end of a ©case© clause\footnote{
    713709In this section, the term \emph{case clause} refers to either a ©case© or ©default© clause.}
    714710\emph{falls through} to the next ©case© clause in the ©switch© statement;
    715 to exit a ©switch© statement from a ©case© clause requires explicitly terminating the clause with a transfer statement, most commonly \Indexc{break}:
     711to exit a ©switch© statement from a ©case© clause requires explicitly terminating the clause with a transfer statement, most commonly ©break©:
    716712\begin{cfa}
    717713switch ( i ) {
    718714  case 1:
    719715        ...
    720         $\R{\LstCommentStyle{// fall-through}}$
     716        // fall-through
    721717  case 2:
    722718        ...
    723         ®break;®        // exit switch statement
     719        break;  // exit switch statement
    724720}
    725721\end{cfa}
     
    767763}
    768764\end{cfa}
    769 This situation is better handled by a list of case values \see{\VRef{s:caseClause}}.
     765This situation better handled without fall-through by allowing a list of case values \see{\VRef{s:caseClause}}.
    770766While fall-through itself is not a problem, the problem occurs when fall-through is the default, as this semantics is unintuitive to many programmers and is different from most programming languages with a ©switch© statement.
    771767Hence, default fall-through semantics results in a large number of programming errors as programmers often \emph{forget} the ©break© statement at the end of a ©case© clause, resulting in inadvertent fall-through.
     
    781777                ...
    782778        } // if
     779  case 2:
     780        while ( j < 5 ) {
     781                ...
     782          ®case 3:®             // transfer into "while" statement
     783                ...
     784        } // while
     785} // switch
    783786\end{cfa}
    784787This usage branches into control structures, which is known to cause both comprehension and technical difficulties.
     
    786789The technical problem results from the inability to ensure declaration and initialization of variables when blocks are not entered at the beginning.
    787790There are few arguments for this kind of control flow, and therefore, there is a strong impetus to eliminate it.
    788 
    789 This C idiom is known as ``\Index*{Duff's device}''~\cite{Duff83}, from this example:
     791Nevertheless, C does have an idiom where this capability is used, known as ``\Index*{Duff's device}''~\cite{Duff83}:
    790792\begin{cfa}
    791793register int n = (count + 7) / 8;
     
    856858still works.
    857859Nevertheless, reversing the default action would have a non-trivial effect on case actions that compound, such as the above example of processing shell arguments.
    858 Therefore, to preserve backwards compatibility, it is necessary to introduce a new kind of ©switch© statement, called \Indexc{choose}, with no implicit fall-through semantics and an explicit fall-through if the last statement of a case-clause ends with the new keyword \Indexc{fallthrough}/\Indexc{fallthru}, \eg:
     860Therefore, to preserve backwards compatibility, it is necessary to introduce a new kind of ©switch© statement, called ©choose©, with no implicit fall-through semantics and an explicit fall-through if the last statement of a case-clause ends with the new keyword ©fallthrough©/©fallthru©, \eg:
    859861\begin{cfa}
    860862®choose® ( i ) {
     
    883885Therefore, no change is made for this issue.
    884886\item
    885 Dealing with unreachable code in a ©switch©/©choose© body is solved by restricting declarations and initialization to the start of statement body, which is executed \emph{before} the transfer to the appropriate ©case© clause\footnote{
     887Dealing with unreachable code in a ©switch©/©choose© body is solved by restricting declarations and associated initialization to the start of statement body, which is executed \emph{before} the transfer to the appropriate ©case© clause\footnote{
    886888Essentially, these declarations are hoisted before the ©switch©/©choose© statement and both declarations and statement are surrounded by a compound statement.} and precluding statements before the first ©case© clause.
    887889Further declarations at the same nesting level as the statement body are disallowed to ensure every transfer into the body is sound.
     
    906908\subsection{Non-terminating and Labelled \texorpdfstring{\LstKeywordStyle{fallthrough}}{Non-terminating and Labelled fallthrough}}
    907909
    908 The \Indexc{fallthrough} clause may be non-terminating within a \Indexc{case} clause or have a target label to common code from multiple case clauses.
     910The ©fallthrough© clause may be non-terminating within a ©case© clause or have a target label to common code from multiple case clauses.
    909911\begin{center}
    910912\begin{tabular}{@{}lll@{}}
     
    958960\end{tabular}
    959961\end{center}
    960 The target label must be below the \Indexc{fallthrough} and may not be nested in a control structure, and
    961 the target label must be at the same or higher level as the containing \Indexc{case} clause and located at
    962 the same level as a ©case© clause; the target label may be case \Indexc{default}, but only associated
    963 with the current \Indexc{switch}/\Indexc{choose} statement.
     962The target label must be below the ©fallthrough© and may not be nested in a control structure, and
     963the target label must be at the same or higher level as the containing ©case© clause and located at
     964the same level as a ©case© clause; the target label may be case ©default©, but only associated
     965with the current ©switch©/©choose© statement.
    964966
    965967\begin{figure}
     
    10741076Looping a fixed number of times, possibly with a loop index, occurs frequently.
    10751077\CFA condenses simply looping to facilitate coding speed and safety.
    1076 The \Indexc{for}, \Indexc{while}, and \Indexc{do} loop-control is augmented as follows \see{examples in \VRef[Figure]{f:LoopControlExamples}}:
     1078The ©for©/©while©/©do-while© loop-control is augmented as follows \see{examples in \VRef[Figure]{f:LoopControlExamples}}:
    10771079\begin{itemize}[itemsep=0pt]
    10781080\item
     
    11431145\subsection{\texorpdfstring{Labelled \LstKeywordStyle{continue} / \LstKeywordStyle{break} Statement}{Labelled continue / break Statement}}
    11441146
    1145 C \Indexc{continue} and \Indexc{break} statements, for altering control flow, are restricted to one level of nesting for a particular control structure.
     1147C ©continue© and ©break© statements, for altering control flow, are restricted to one level of nesting for a particular control structure.
    11461148This restriction forces programmers to use \Indexc{goto} to achieve the equivalent control-flow for more than one level of nesting.
    11471149To prevent having to switch to the ©goto©, \CFA extends the \Indexc{continue}\index{continue@©continue©!labelled}\index{labelled!continue@©continue©} and \Indexc{break}\index{break@©break©!labelled}\index{labelled!break@©break©} with a target label to support static multi-level exit\index{multi-level exit}\index{static multi-level exit}~\cite{Buhr85}, as in Java.
    1148 For both ©continue© and ©break©, the target label must be directly associated with a \Indexc{for}, \Indexc{while} or \Indexc{do} statement;
    1149 for ©break©, the target label can also be associated with a \Indexc{switch}, \Indexc{if} or compound (©{}©) statement.
     1150For both ©continue© and ©break©, the target label must be directly associated with a ©for©, ©while© or ©do© statement;
     1151for ©break©, the target label can also be associated with a ©switch©, ©if© or compound (©{}©) statement.
    11501152\VRef[Figure]{f:MultiLevelExit} shows a comparison between labelled ©continue© and ©break© and the corresponding C equivalent using ©goto© and labels.
    11511153The innermost loop has 8 exit points, which cause continuation or termination of one or more of the 7 \Index{nested control-structure}s.
     
    12221224\end{figure}
    12231225
    1224 Both labelled \Indexc{continue} and \Indexc{break} are a \Indexc{goto}\index{goto@©goto©!restricted} restricted in the following ways:
     1226Both labelled ©continue© and ©break© are a ©goto©\index{goto@©goto©!restricted} restricted in the following ways:
    12251227\begin{itemize}
    12261228\item
     
    12381240
    12391241
    1240 \subsection{\texorpdfstring{Extended \LstKeywordStyle{else}}{Extended else}}
    1241 \label{s:ExtendedElse}
    1242 \index{extended ©else©}
    1243 
    1244 The ©if© statement has an optional ©else© clause executed if the conditional is false.
    1245 This concept is extended to the \Indexc{while}, \Indexc{for}, and \Indexc{do} looping constructs (like Python).
    1246 Hence, if the loop conditional becomes false, looping stops and the corresponding ©else© clause is executed, if present.
    1247 
    1248 The following example is a linear search for the key 3 in an array, where finding the key is handled with a ©break© and not finding with the ©else© clause on the loop construct.
    1249 \begin{cquote}
    1250 \begin{cfa}
    1251 int a[10];
    1252 \end{cfa}
    1253 \begin{tabular}{@{}lll@{}}
    1254 \begin{cfa}
    1255 
    1256 while ( int i = 0; i < 10 ) {
    1257   if ( a[i] == 3 ) break; // found
    1258         i += 1;
    1259 } ®else® { // i == 10
    1260         sout | "not found";
    1261 }
    1262 \end{cfa}
    1263 &
    1264 \begin{cfa}
    1265 
    1266 for ( i; 10 ) {
    1267   if ( a[i] == 3 ) break; // found
    1268 
    1269 } ®else® { // i == 10
    1270         sout | "not found";
    1271 }
    1272 \end{cfa}
    1273 &
    1274 \begin{cfa}
    1275 int i = 0;
    1276 do {
    1277   if ( a[i] == 3 ) break; // found
    1278         i += 1;
    1279 } while( i < 10 ) ®else® { // i == 10
    1280         sout | "not found";
    1281 }
    1282 \end{cfa}
    1283 \end{tabular}
    1284 \end{cquote}
    1285 Note, \Index{dangling else} now occurs with \Indexc{if}, \Indexc{while}, \Indexc{for}, \Indexc{do}, and \Indexc{waitfor}.
    1286 
    1287 
    12881242%\subsection{\texorpdfstring{\protect\lstinline{with} Statement}{with Statement}}
    12891243\subsection{\texorpdfstring{\LstKeywordStyle{with} Statement}{with Statement}}
     
    13121266Therefore, reducing aggregate qualification is a useful language design goal.
    13131267
    1314 C partially addresses the problem by eliminating qualification for enumerated types and unnamed \emph{nested} aggregates, which open their scope into the containing aggregate.
     1268C allows unnamed nested aggregates that open their scope into the containing aggregate.
    13151269This feature is used to group fields for attributes and/or with ©union© aggregates.
    13161270\begin{cfa}
    13171271struct S {
    1318         struct $\R{\LstCommentStyle{/* unnamed */}}$ { int g,  h; } __attribute__(( aligned(64) ));
     1272        struct { int g,  h; } __attribute__(( aligned(64) ));
    13191273        int tag;
    1320         union $\R{\LstCommentStyle{/* unnamed */}}$ {
     1274        union {
    13211275                struct { char c1,  c2; } __attribute__(( aligned(128) ));
    13221276                struct { int i1,  i2; };
    13231277                struct { double d1,  d2; };
    13241278        };
    1325 } s;
    1326 enum { R, G, B };
    1327 s.g; s.h;   s.tag = R;   s.c1; s.c2;   s.i1 = G; s.i2 = B;   s.d1; s.d2;
     1279};
     1280s.g; s.h; s.tag; s.c1; s.c2; s.i1; s.i2; s.d1; s.d2;
    13281281\end{cfa}
    13291282
     
    13701323\end{cfa}
    13711324where qualification is only necessary to disambiguate the shadowed variable ©i©.
    1372 In detail, the ©with© statement may form a function body or be nested within a function body.
    1373 
     1325
     1326In detail, the ©with© statement may appear as the body of a function or nested within a function body.
    13741327The ©with© clause takes a list of expressions, where each expression provides an aggregate type and object.
    13751328(Enumerations are already opened.)
     
    13801333\end{cfa}
    13811334The expression object is the implicit qualifier for the open structure-fields.
    1382 
    13831335\CFA's ability to overload variables \see{\VRef{s:VariableOverload}} and use the left-side of assignment in type resolution means most fields with the same name but different types are automatically disambiguated, eliminating qualification.
    13841336All expressions in the expression list are open in parallel within the compound statement.
     
    14101362\end{cfa}
    14111363A cast or qualification can be used to disambiguate variables within a ©with© \emph{statement}.
    1412 A cast can also be used to disambiguate among overload variables in a ©with© \emph{expression}:
     1364A cast can be used to disambiguate among overload variables in a ©with© \emph{expression}:
    14131365\begin{cfa}
    14141366with ( w ) { ... }                                                      $\C{// ambiguous, same name and no context}$
     
    14191371Finally, there is an interesting problem between parameters and the function-body ©with©, \eg:
    14201372\begin{cfa}
    1421 void f( S & s, char c ) with ( s ) {
    1422         ®s.c = c;®  i = 3;  d = 5.5;                    $\C{// initialize fields}$
    1423 }
    1424 \end{cfa}
    1425 Here, the assignment ©s.c = c© means ©s.c = s.c©, which is meaningless, and there is no mechanism to qualify the parameter ©c©, making the assignment impossible using the function-body ©with©.
    1426 To solve this problem, parameters \emph{not} explicitly opened are treated like an initialized aggregate:
    1427 \begin{cfa}
    1428 struct Params {                                                         $\C{// s explicitly opened so S \& s elided}$
    1429         char c;
     1373void ?{}( S & s, int i ) with ( s ) { $\C{// constructor}$
     1374        ®s.i = i;®  j = 3;  m = 5.5; $\C{// initialize fields}$
     1375}
     1376\end{cfa}
     1377Here, the assignment ©s.i = i© means ©s.i = s.i©, which is meaningless, and there is no mechanism to qualify the parameter ©i©, making the assignment impossible using the function-body ©with©.
     1378To solve this problem, parameters are treated like an initialized aggregate:
     1379\begin{cfa}
     1380struct Params {
     1381        S & s;
     1382        int i;
    14301383} params;
    14311384\end{cfa}
    14321385and implicitly opened \emph{after} a function-body open, to give them higher priority:
    14331386\begin{cfa}
    1434 void f( S & s, char ®c® ) with ( s ) ®with( $\emph{\R{params}}$ )® { // syntax not allowed, illustration only
    1435         s.c = ®c;®  i = 3;  d = 5.5;
     1387void ?{}( S & s, int ®i® ) with ( s ) ®with( $\emph{\R{params}}$ )® { // syntax not allowed, illustration only
     1388        s.i = ®i®; j = 3; m = 5.5;
    14361389}
    14371390\end{cfa}
    14381391This implicit semantic matches with programmer expectation.
     1392
    14391393
    14401394
     
    34433397This requirement is the same as for comma expressions in argument lists.
    34443398
    3445 Type qualifiers, \ie ©const© and ©volatile©, may modify a tuple type.
    3446 The meaning is to distribute the qualifier across all of the types in the tuple, \eg:
     3399Type qualifiers, \ie const and volatile, may modify a tuple type.
     3400The meaning is the same as for a type qualifier modifying an aggregate type [Int99, x 6.5.2.3(7),x 6.7.3(11)], \ie the qualifier is distributed across all of the types in the tuple, \eg:
    34473401\begin{cfa}
    34483402const volatile [ int, float, const int ] x;
     
    36433597Stream ©exit© implicitly returns ©EXIT_FAILURE© to the shell.
    36443598\begin{cfa}
    3645 ®exit®   | "x (" | x | ") negative value.";   // terminate and return EXIT_FAILURE to shell
    3646 ®abort® | "x (" | x | ") negative value.";   // terminate and generate stack trace and core file
     3599®exit®   | "x (" | x | ") negative value."; // terminate and return EXIT_FAILURE to shell
     3600®abort® | "x (" | x | ") negative value."; // terminate and generate stack trace and core file
    36473601\end{cfa}
    36483602Note, \CFA stream variables ©stdin©, ©stdout©, ©stderr©, ©exit©, and ©abort© overload C variables ©stdin©, ©stdout©, ©stderr©, and functions ©exit© and ©abort©, respectively.
     
    43134267        sout | '1' | '2' | '3';
    43144268        sout | 1 | "" | 2 | "" | 3;
    4315         sout | "x (" | 1 | "x [" | 2 | "x {" | 3 | "x =" | 4 | "x $" | 5 | "x £" | 6 | "x Â¥"
    4316                 | 7 | "x ¡" | 8 | "x ¿" | 9 | "x «" | 10;
     4269        sout | "x (" | 1 | "x [" | 2 | "x {" | 3 | "x =" | 4 | "x $" | 5 | "x £" | 6 | "x ¥"
     4270                | 7 | "x ¡" | 8 | "x ¿" | 9 | "x «" | 10;
    43174271        sout | 1 | ", x" | 2 | ". x" | 3 | "; x" | 4 | "! x" | 5 | "? x" | 6 | "% x"
    4318                 | 7 | "¢ x" | 8 | "» x" | 9 | ") x" | 10 | "] x" | 11 | "} x";
     4272                | 7 | "¢ x" | 8 | "» x" | 9 | ") x" | 10 | "] x" | 11 | "} x";
    43194273        sout | "x`" | 1 | "`x'" | 2 | "'x\"" | 3 | "\"x:" | 4 | ":x " | 5 | " x\t" | 6 | "\tx";
    43204274        sout | "x ( " | 1 | " ) x" | 2 | " , x" | 3 | " :x: " | 4;
     
    44924446The common usage is the short form of the mutex statement\index{ostream@©ostream©!mutex@©mutex©} to lock a stream during a single cascaded I/O expression, \eg:
    44934447\begin{cfa}
    4494 $\emph{thread\(_1\)}$ : ®mutex( sout )® sout | "abc " | "def ";
    4495 $\emph{thread\(_2\)}$ : ®mutex( sout )® sout | "uvw " | "xyz ";
     4448$\emph{thread\(_1\)}$ : ®mutex()® sout | "abc " | "def ";
     4449$\emph{thread\(_2\)}$ : ®mutex()® sout | "uvw " | "xyz ";
    44964450\end{cfa}
    44974451Now, the order of the thread execution is still non-deterministic, but the output is constrained to two possible lines in either order.
     
    45164470®mutex( sout )® {
    45174471        sout | 1;
    4518         ®mutex( sout ) sout® | 2 | 3;                           $\C{// unnecessary, but ok because of recursive lock}$
     4472        ®mutex() sout® | 2 | 3;                         $\C{// unnecessary, but ok because of recursive lock}$
    45194473        sout | 4;
    45204474} // implicitly release sout lock
     
    45284482        int x, y, z, w;
    45294483        sin | x;
    4530         ®mutex( sin )® sin | y | z;                                     $\C{// unnecessary, but ok because of recursive lock}$
     4484        ®mutex() sin® | y | z;                          $\C{// unnecessary, but ok because of recursive lock}$
    45314485        sin | w;
    45324486} // implicitly release sin lock
     
    45374491\Textbf{WARNING:} The general problem of \Index{nested locking} can occur if routines are called in an I/O sequence that block, \eg:
    45384492\begin{cfa}
    4539 ®mutex( sout )® sout | "data:" | rtn( mon );    $\C{// mutex call on monitor}$
     4493®mutex() sout® | "data:" | rtn( mon );  $\C{// mutex call on monitor}$
    45404494\end{cfa}
    45414495If the thread executing the I/O expression blocks in the monitor with the ©sout© lock, other threads writing to ©sout© also block until the thread holding the lock is unblocked and releases it.
     
    45444498\begin{cfa}
    45454499int ®data® = rtn( mon );
    4546 mutex( sout ) sout | "data:" | ®data®;
    4547 \end{cfa}
    4548 
    4549 
    4550 \subsection{Locale}
    4551 \index{stream!locale}
    4552 \index{locale!stream}
    4553 
    4554 Cultures use different syntax, called a \newterm{locale}, for printing numbers so they are easier to read, \eg:
    4555 \begin{cfa}
    4556 12®,®345®.®123          $\C[1.25in]{// comma separator, period decimal-point}$
    4557 12®.®345®,®123          $\C{// period separator, comma decimal-point}$
    4558 12$\Sp$345®,®123®.®     $\C{// space separator, comma decimal-point, period terminator}\CRT$
    4559 \end{cfa}
    4560 A locale is selected with function ©setlocale©, and the corresponding locale package \emph{must} be installed on the underlying system;
    4561 ©setlocale© returns ©0p© if the requested locale is unavailable.
    4562 Furthermore, a locale covers the syntax for many cultural items, \eg address, measurement, money, etc.
    4563 This discussion applies to item ©LC_NUMERIC© for formatting non-monetary integral and floating-point values.
    4564 \VRef[Figure]{f:StreamLocale} shows selecting different cultural syntax, which may be associated with one or more countries.
    4565 
    4566 \begin{figure}
    4567 \begin{cfa}
    4568 #include <fstream.hfa>
    4569 #include <locale.h>                                                     $\C{// setlocale}$
    4570 #include <stdlib.h>                                                     $\C{// getenv}$
    4571 
    4572 int main() {
    4573         void print() {
    4574                 sout | 12 | 123 | 1234 | 12345 | 123456 | 1234567;
    4575                 sout | 12. | 123.1 | 1234.12 | 12345.123 | 123456.1234 | 1234567.12345;
    4576                 sout | nl;
    4577         }
    4578         sout | "Default locale off";
    4579         print();
    4580         sout | "Locale on" | ®setlocale( LC_NUMERIC, getenv( "LANG" ) )®;  // enable local locale
    4581         print();
    4582         sout | "German" | ®setlocale( LC_NUMERIC, "de_DE.UTF-8" )®;  // enable German locale
    4583         print();
    4584         sout | "Ukraine" | ®setlocale( LC_NUMERIC, "uk_UA.utf8" )®;  // enable Ukraine locale
    4585         print();
    4586         sout | "Default locale off" | ®setlocale( LC_NUMERIC, "C" )®;  // disable locale
    4587         print();
    4588 }
    4589 
    4590 Default locale off
    4591 12 123 1234 12345 123456 1234567
    4592 12. 123.1 1234.12 12345.123 123456.1234 1234567.12345
    4593 
    4594 Locale on en_US.UTF-8
    4595 12 123 1®,®234 12®,®345 123®,®456 1®,®234®,®567
    4596 12®.® 123®.®1 1®,®234®.®12 12®,®345®.®123 123®,®456®.®1234 1®,®234®,®567®.®12345
    4597 
    4598 German de_DE.UTF-8
    4599 12 123 1®.®234 12®.®345 123®.®456 1®.®234®.®567
    4600 12®.® 123®,®1®.® 1®.®234®,®12 12®.®345®,®123 123®.®456®,®1234 1®.®234®.®567®,®12345
    4601 
    4602 Ukraine uk_UA.utf8
    4603 12 123 1 234 12 345 123 456 1 234 567
    4604 12®.® 123®,®1®.® 1$\Sp$234®,®12®.® 12$\Sp$ 345®,®123®.® 123$\Sp$ 456®,®1234®.® 1$\Sp$ 234$\Sp$567®,®12345®.®
    4605 
    4606 Default locale off C
    4607 12 123 1234 12345 123456 1234567
    4608 12. 123.1 1234.12 12345.123 123456.1234 1234567.12345
    4609 \end{cfa}
    4610 \caption{Stream Locale}
    4611 \label{f:StreamLocale}
    4612 \end{figure}
     4500mutex() sout | "data:" | ®data®;
     4501\end{cfa}
    46134502
    46144503
     
    46664555\end{figure}
    46674556
    4668 
    46694557\begin{comment}
    46704558\section{Types}
     
    47494637
    47504638
    4751 \section{Structures}
     4639\subsection{Structures}
    47524640
    47534641Structures in \CFA are basically the same as structures in C.
     
    53825270\subsection{Coroutine}
    53835271
    5384 \Index{Coroutines} are the precursor to threads.
     5272\Index{Coroutines} are the precursor to tasks.
    53855273\VRef[Figure]{f:FibonacciCoroutine} shows a coroutine that computes the \Index*{Fibonacci} numbers.
    53865274
     
    54845372
    54855373
    5486 \subsection{Threads}
     5374\subsection{Tasks}
    54875375
    54885376\CFA also provides a simple mechanism for creating and utilizing user level threads.
    5489 A thread provides mutual exclusion like a monitor, and also has its own execution state and a thread of control.
    5490 Similar to a monitor, a thread is defined like a structure:
     5377A task provides mutual exclusion like a monitor, and also has its own execution state and a thread of control.
     5378Similar to a monitor, a task is defined like a structure:
    54915379
    54925380\begin{figure}
     
    55325420}
    55335421\end{cfa}
    5534 \caption{Simple Threads}
    5535 \label{f:SimpleThreads}
     5422\caption{Simple Tasks}
     5423\label{f:SimpleTasks}
    55365424\end{figure}
    55375425
     
    69006788In \CFA, there are ambiguous cases with dereference and operator identifiers, \eg ©int *?*?()©, where the string ©*?*?© can be interpreted as:
    69016789\begin{cfa}
    6902 *?$\Sp$*? $\C{// dereference operator, dereference operator}$
    6903 *$\Sp$?*? $\C{// dereference, multiplication operator}$
     6790*?$\R{\textvisiblespace}$*? $\C{// dereference operator, dereference operator}$
     6791*$\R{\textvisiblespace}$?*? $\C{// dereference, multiplication operator}$
    69046792\end{cfa}
    69056793By default, the first interpretation is selected, which does not yield a meaningful parse.
     
    69256813Therefore, it is necessary to disambiguate these cases with a space:
    69266814\begin{cfa}
    6927 i++$\Sp$? i : 0;
    6928 i?$\Sp$++i : 0;
     6815i++$\R{\textvisiblespace}$? i : 0;
     6816i?$\R{\textvisiblespace}$++i : 0;
    69296817\end{cfa}
    69306818
     
    75427430char random( void );$\indexc{random}$
    75437431char random( char u ); $\C{// [0,u)}$
    7544 char random( char l, char u ); $\C{// [l,u]}$
     7432char random( char l, char u ); $\C{// [l,u)}$
    75457433int random( void );
    75467434int random( int u ); $\C{// [0,u)}$
    7547 int random( int l, int u ); $\C{// [l,u]}$
     7435int random( int l, int u ); $\C{// [l,u)}$
    75487436unsigned int random( void );
    75497437unsigned int random( unsigned int u ); $\C{// [0,u)}$
    7550 unsigned int random( unsigned int l, unsigned int u ); $\C{// [l,u]}$
     7438unsigned int random( unsigned int l, unsigned int u ); $\C{// [l,u)}$
    75517439long int random( void );
    75527440long int random( long int u ); $\C{// [0,u)}$
    7553 long int random( long int l, long int u ); $\C{// [l,u]}$
     7441long int random( long int l, long int u ); $\C{// [l,u)}$
    75547442unsigned long int random( void );
    75557443unsigned long int random( unsigned long int u ); $\C{// [0,u)}$
    7556 unsigned long int random( unsigned long int l, unsigned long int u ); $\C{// [l,u]}$
     7444unsigned long int random( unsigned long int l, unsigned long int u ); $\C{// [l,u)}$
    75577445float random( void );                                            $\C{// [0.0, 1.0)}$
    75587446double random( void );                                           $\C{// [0.0, 1.0)}$
     
    82188106
    82198107
    8220 \section{Pseudo Random Number Generator}
    8221 \label{s:PRNG}
    8222 
    8223 Random numbers are values generated independently, i.e., new values do not depend on previous values (independent trials), \eg lottery numbers, shuffled cards, dice roll, coin flip.
    8224 While a primary goal of programming is computing values that are \emph{not} random, random values are useful in simulation, cryptography, games, etc.
    8225 A random-number generator is an algorithm that computes independent values.
    8226 If the algorithm uses deterministic computation (a predictable sequence of values), it generates \emph{pseudo} random numbers versus \emph{true} random numbers.
    8227 
    8228 All \newterm{pseudo random-number generators} (\newterm{PRNG}) involve some technique to scramble bits of a value, \eg multiplicative recurrence:
    8229 \begin{cfa}
    8230 rand = 36973 * (rand & 65535) + (rand >> 16); // scramble bits
    8231 \end{cfa}
    8232 Multiplication of large values adds new least-significant bits and drops most-significant bits.
    8233 \begin{quote}
    8234 \begin{tabular}{@{}r|l@{}}
    8235 bits 63--32 (most)      & bits 31--0 (least)    \\
    8236 \hline
    8237 0x0                                     & 0x3e8e36                              \\
    8238 0x5f                            & 0x718c25e1                    \\
    8239 0xad3e                          & 0x7b5f1dbe                    \\
    8240 0xbc3b                          & 0xac69ff19                    \\
    8241 0x1070f                         & 0x2d258dc6                    \\
    8242 \end{tabular}
    8243 \end{quote}
    8244 By dropping bits 63--32, bits 31--0 become scrambled after each multiply.
    8245 The least-significant bits \emph{appear} random but the same bits are always generated given a fixed starting value, called the \newterm{seed} (value 0x3e8e36 above).
    8246 Hence, if a program uses the same seed, the same sequence of pseudo-random values is generated from the PRNG.
    8247 Often the seed is set to another random value like a program's process identifier (©getpid©\index{getpid@©getpid©}) or time when the program is run;
    8248 hence, one random value bootstraps another.
    8249 Finally, a PRNG usually generates a range of large values, \eg ©[0, UINT_MAX]©, which are scaled using the modulus operator, \eg ©prng() % 5© produces random values in the range 0--4.
    8250 
    8251 \CFA provides a sequential PRNG type only accessible by a single thread (not thread-safe) and a set of global and companion thread PRNG functions accessible by multiple threads without contention.
    8252 \begin{itemize}
    8253 \item
    8254 The ©PRNG© type is for sequential programs, like coroutining:
    8255 \begin{cfa}
    8256 struct PRNG { ... }; $\C[3.75in]{// opaque type}$
    8257 void ?{}( PRNG & prng ); $\C{// random seed}$
    8258 void ?{}( PRNG & prng, uint32_t seed ); $\C{// fixed seed}$
    8259 void set_seed( PRNG & prng, uint32_t seed ); $\C{// set seed}$
    8260 uint32_t get_seed( PRNG & prng ); $\C{// get seed}$
    8261 uint32_t prng( PRNG & prng ); $\C{// [0,UINT\_MAX]}$
    8262 uint32_t prng( PRNG & prng, uint32_t u ); $\C{// [0,u)}$
    8263 uint32_t prng( PRNG & prng, uint32_t l, uint32_t u ); $\C{// [l,u]}$
    8264 uint32_t calls( PRNG & prng ); $\C{// number of calls}\CRT$
    8265 \end{cfa}
    8266 A ©PRNG© object is used to randomize behaviour or values during execution, \eg in games, a character makes a random move or an object takes on a random value.
    8267 In this scenario, it is useful to have multiple ©PRNG© objects, \eg one per player or object.
    8268 However, sequential execution is still repeatable given the same starting seeds for all ©PRNG©s.
    8269 \VRef[Figure]{f:SequentialPRNG} shows an example that creates two sequential ©PRNG©s, sets both to the same seed (1009), and illustrates the three forms for generating random values, where both ©PRNG©s generate the same sequence of values.
    8270 
    8271 \begin{figure}
    8272 \begin{cfa}
    8273 PRNG prng1, prng2;
    8274 ®set_seed( prng1, 1009 )®;   ®set_seed( prng2, 1009 )®;
    8275 for ( 10 ) {
    8276         // Do not cascade prng calls because side-effect functions called in arbitrary order.
    8277         sout | nlOff | ®prng( prng1 )®;  sout | ®prng( prng1, 5 )®;  sout | ®prng( prng1, 0, 5 )® | '\t';
    8278         sout | ®prng( prng2 )®;  sout | ®prng( prng2, 5 )®;  sout | ®prng( prng2, 0, 5 )® | nlOn;
    8279 }
    8280 \end{cfa}
    8281 \begin{cquote}
    8282 \begin{tabular}{@{}ll@{}}
    8283 \begin{cfa}
    8284 37301721 2 2
    8285 1681308562 1 3
    8286 290112364 3 2
    8287 1852700364 4 3
    8288 733221210 1 3
    8289 1775396023 2 3
    8290 123981445 2 3
    8291 2062557687 2 0
    8292 283934808 1 0
    8293 672325890 1 3
    8294 \end{cfa}
    8295 &
    8296 \begin{cfa}
    8297 37301721 2 2
    8298 1681308562 1 3
    8299 290112364 3 2
    8300 1852700364 4 3
    8301 733221210 1 3
    8302 1775396023 2 3
    8303 123981445 2 3
    8304 2062557687 2 0
    8305 283934808 1 0
    8306 672325890 1 3
    8307 \end{cfa}
    8308 \end{tabular}
    8309 \end{cquote}
    8310 \caption{Sequential PRNG}
    8311 \label{f:SequentialPRNG}
    8312 \end{figure}
    8313 
    8314 \item
    8315 The PRNG global and companion thread functions are for concurrent programming, such as randomizing execution in short-running programs, \eg ©yield( prng() % 5 )©.
    8316 \begin{cfa}
    8317 void set_seed( uint32_t seed ); $\C[3.75in]{// set global seed}$
    8318 uint32_t get_seed(); $\C{// get global seed}$
    8319 // SLOWER
    8320 uint32_t prng(); $\C{// [0,UINT\_MAX]}$
    8321 uint32_t prng( uint32_t u ); $\C{// [0,u)}$
    8322 uint32_t prng( uint32_t l, uint32_t u ); $\C{// [l,u]}$
    8323 // FASTER
    8324 uint32_t prng( $thread\LstStringStyle{\textdollar}$ & th );     $\C{// [0,UINT\_MAX]}$
    8325 uint32_t prng( $thread\LstStringStyle{\textdollar}$ & th, uint32_t u ); $\C{// [0,u)}$
    8326 uint32_t prng( $thread\LstStringStyle{\textdollar}$ & th, uint32_t l, uint32_t u );     $\C{// [l,u]}\CRT$
    8327 \end{cfa}
    8328 The only difference between the two sets of ©prng© routines is performance.
    8329 
    8330 Because concurrent execution is non-deterministic, seeding the concurrent PRNG is less important, as repeatable execution is impossible.
    8331 Hence, there is one system-wide PRNG (global seed) but each \CFA thread has its own non-contended PRNG state.
    8332 If the global seed is set, threads start with this seed, until it is reset and then threads start with the reset seed.
    8333 Hence, these threads generate the same sequence of random numbers from their specific starting seed.
    8334 If the global seed is \emph{not} set, threads start with a random seed, until the global seed is set.
    8335 Hence, these threads generate different sequences of random numbers.
    8336 If each thread needs its own seed, use a sequential ©PRNG© in each thread.
    8337 The slower ©prng© functions \emph{without} a thread argument call ©active_thread© internally to indirectly access the current thread's PRNG state, while the faster ©prng© functions \emph{with} a thread argument directly access the thread through the thread parameter.
    8338 If a thread pointer is available, \eg in thread main, eliminating the call to ©active_thread© significantly reduces the cost of accessing the thread's PRNG state.
    8339 \VRef[Figure]{f:ConcurrentPRNG} shows an example using the slower/faster concurrent PRNG in the program main and a thread.
    8340 
    8341 \begin{figure}
    8342 \begin{cfa}
    8343 thread T {};
    8344 void main( ®T & th® ) {  // thread address
    8345         for ( i; 10 ) {
    8346                 sout | nlOff | ®prng()®;  sout | ®prng( 5 )®;  sout | ®prng( 0, 5 )® | '\t';  // SLOWER
    8347                 sout | nlOff | ®prng( th )®;  sout | ®prng( th, 5 )®;  sout | ®prng( th, 0, 5 )® | nlOn;  // FASTER
    8348         }
    8349 }
    8350 int main() {
    8351         set_seed( 1009 );
    8352         $\R{thread\LstStringStyle{\textdollar}}$ ®& th = *active_thread()®;  // program-main thread-address
    8353         for ( i; 10 ) {
    8354                 sout | nlOff | ®prng()®; sout | ®prng( 5 )®; sout | ®prng( 0, 5 )® | '\t';  // SLOWER
    8355                 sout | nlOff | ®prng( th )®; sout | ®prng( th, 5 )®; sout | ®prng( th, 0, 5 )® | nlOn;  // FASTER
    8356         }
    8357         sout | nl;
    8358         T t; // run thread
    8359 }
    8360 \end{cfa}
    8361 \begin{cquote}
    8362 \begin{tabular}{@{}ll@{}}
    8363 \begin{cfa}
    8364 37301721 2 2
    8365 290112364 3 2
    8366 733221210 1 3
    8367 123981445 2 3
    8368 283934808 1 0
    8369 1414344101 1 3
    8370 871831898 3 4
    8371 2142057611 4 4
    8372 802117363 0 4
    8373 2346353643 1 3
    8374 \end{cfa}
    8375 &
    8376 \begin{cfa}
    8377 1681308562 1 3
    8378 1852700364 4 3
    8379 1775396023 2 3
    8380 2062557687 2 0
    8381 672325890 1 3
    8382 873424536 3 4
    8383 866783532 0 1
    8384 17310256 2 5
    8385 492964499 0 0
    8386 2143013105 3 2
    8387 \end{cfa}
    8388 \end{tabular}
    8389 \begin{cfa}
    8390 // same output as above from thread t
    8391 \end{cfa}
    8392 \end{cquote}
    8393 \caption{Concurrent PRNG}
    8394 \label{f:ConcurrentPRNG}
    8395 \end{figure}
    8396 \end{itemize}
    8397 
    8398 
    83998108\section{Multi-precision Integers}
    84008109\label{s:MultiPrecisionIntegers}
     
    86018310\end{tabular}
    86028311\end{cquote}
    8603 
     8312\small
    86048313\begin{cfa}
    86058314Factorial Numbers
  • driver/cc1.cc

    r92538ab r4559b34  
    1010// Created On       : Fri Aug 26 14:23:51 2005
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Feb 17 18:04:23 2022
    13 // Update Count     : 422
     12// Last Modified On : Wed Jul 21 09:46:24 2021
     13// Update Count     : 419
    1414//
    1515
     
    6161static string __CFA_FLAGPREFIX__( "__CFA_FLAG" );               // "__CFA_FLAG__=" suffix
    6262
    63 static void checkEnv1() {                                                               // stage 1
     63static void checkEnv1( const char * args[], int & nargs ) { // stage 1
    6464        extern char ** environ;
    6565
     
    155155        cerr << "Stage1" << endl;
    156156        #endif // __DEBUG_H__
    157         checkEnv1();                                                                            // arguments passed via environment variables
     157        checkEnv1( args, nargs );                                                       // arguments passed via environment variables
    158158        #ifdef __DEBUG_H__
    159159        for ( int i = 1; i < argc; i += 1 ) {
  • libcfa/src/Makefile.am

    r92538ab r4559b34  
    5858        bits/queue.hfa \
    5959        bits/sequence.hfa \
     60        containers/array.hfa \
    6061        concurrency/iofwd.hfa \
    61         concurrency/barrier.hfa \
    62         containers/array.hfa \
    6362        containers/list.hfa \
    6463        containers/queueLockFree.hfa \
    6564        containers/stackLockFree.hfa \
    66         containers/string_sharectx.hfa \
    6765        containers/vector2.hfa \
    6866        vec/vec.hfa \
     
    120118        concurrency/exception.hfa \
    121119        concurrency/kernel.hfa \
    122         concurrency/kernel/cluster.hfa \
    123120        concurrency/locks.hfa \
    124121        concurrency/monitor.hfa \
     
    136133        concurrency/io/call.cfa \
    137134        concurrency/iofwd.hfa \
    138         concurrency/kernel/private.hfa \
     135        concurrency/kernel_private.hfa \
    139136        concurrency/kernel/startup.cfa \
    140137        concurrency/preemption.cfa \
  • libcfa/src/concurrency/coroutine.cfa

    r92538ab r4559b34  
    2727#include <unwind.h>
    2828
    29 #include "kernel/private.hfa"
     29#include "kernel_private.hfa"
    3030#include "exception.hfa"
    3131#include "math.hfa"
  • libcfa/src/concurrency/io.cfa

    r92538ab r4559b34  
    4141        #include "kernel.hfa"
    4242        #include "kernel/fwd.hfa"
    43         #include "kernel/private.hfa"
    44         #include "kernel/cluster.hfa"
     43        #include "kernel_private.hfa"
    4544        #include "io/types.hfa"
    4645
     
    9493        extern void __kernel_unpark( thread$ * thrd, unpark_hint );
    9594
    96         static void ioring_syscsll( struct $io_context & ctx, unsigned int min_comp, unsigned int flags ) {
    97                 __STATS__( true, io.calls.flush++; )
    98                 int ret;
    99                 for() {
    100                         ret = syscall( __NR_io_uring_enter, ctx.fd, ctx.sq.to_submit, min_comp, flags, (sigset_t *)0p, _NSIG / 8);
     95        bool __cfa_io_drain( processor * proc ) {
     96                /* paranoid */ verify( ! __preemption_enabled() );
     97                /* paranoid */ verify( ready_schedule_islocked() );
     98                /* paranoid */ verify( proc );
     99                /* paranoid */ verify( proc->io.ctx );
     100
     101                // Drain the queue
     102                $io_context * ctx = proc->io.ctx;
     103                unsigned head = *ctx->cq.head;
     104                unsigned tail = *ctx->cq.tail;
     105                const __u32 mask = *ctx->cq.mask;
     106
     107                __u32 count = tail - head;
     108                __STATS__( false, io.calls.drain++; io.calls.completed += count; )
     109
     110                if(count == 0) return false;
     111
     112                for(i; count) {
     113                        unsigned idx = (head + i) & mask;
     114                        volatile struct io_uring_cqe & cqe = ctx->cq.cqes[idx];
     115
     116                        /* paranoid */ verify(&cqe);
     117
     118                        struct io_future_t * future = (struct io_future_t *)(uintptr_t)cqe.user_data;
     119                        __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future );
     120
     121                        __kernel_unpark( fulfil( *future, cqe.res, false ), UNPARK_LOCAL );
     122                }
     123
     124                __cfadbg_print_safe(io, "Kernel I/O : %u completed\n", count);
     125
     126                // Mark to the kernel that the cqe has been seen
     127                // Ensure that the kernel only sees the new value of the head index after the CQEs have been read.
     128                __atomic_store_n( ctx->cq.head, head + count, __ATOMIC_SEQ_CST );
     129
     130                /* paranoid */ verify( ready_schedule_islocked() );
     131                /* paranoid */ verify( ! __preemption_enabled() );
     132
     133                return true;
     134        }
     135
     136        bool __cfa_io_flush( processor * proc, int min_comp ) {
     137                /* paranoid */ verify( ! __preemption_enabled() );
     138                /* paranoid */ verify( proc );
     139                /* paranoid */ verify( proc->io.ctx );
     140
     141                __attribute__((unused)) cluster * cltr = proc->cltr;
     142                $io_context & ctx = *proc->io.ctx;
     143
     144                __ioarbiter_flush( ctx );
     145
     146                if(ctx.sq.to_submit != 0 || min_comp > 0) {
     147
     148                        __STATS__( true, io.calls.flush++; )
     149                        int ret = syscall( __NR_io_uring_enter, ctx.fd, ctx.sq.to_submit, min_comp, min_comp > 0 ? IORING_ENTER_GETEVENTS : 0, (sigset_t *)0p, _NSIG / 8);
    101150                        if( ret < 0 ) {
    102151                                switch((int)errno) {
     152                                case EAGAIN:
    103153                                case EINTR:
    104                                         continue;
    105                                 case EAGAIN:
    106154                                case EBUSY:
    107155                                        // Update statistics
     
    112160                                }
    113161                        }
    114                         break;
    115                 }
    116 
    117                 __cfadbg_print_safe(io, "Kernel I/O : %u submitted to io_uring %d\n", ret, ctx.fd);
    118                 __STATS__( true, io.calls.submitted += ret; )
    119                 /* paranoid */ verify( ctx.sq.to_submit <= *ctx.sq.num );
    120                 /* paranoid */ verify( ctx.sq.to_submit >= ret );
    121 
    122                 ctx.sq.to_submit -= ret;
    123 
    124                 /* paranoid */ verify( ctx.sq.to_submit <= *ctx.sq.num );
    125 
    126                 // Release the consumed SQEs
    127                 __release_sqes( ctx );
    128 
    129                 /* paranoid */ verify( ! __preemption_enabled() );
    130 
    131                 __atomic_store_n(&ctx.proc->io.pending, false, __ATOMIC_RELAXED);
    132         }
    133 
    134         static bool try_acquire( $io_context * ctx ) __attribute__((nonnull(1))) {
    135                 /* paranoid */ verify( ! __preemption_enabled() );
    136                 /* paranoid */ verify( ready_schedule_islocked() );
    137 
    138 
    139                 {
    140                         const __u32 head = *ctx->cq.head;
    141                         const __u32 tail = *ctx->cq.tail;
    142 
    143                         if(head == tail) return false;
    144                 }
    145 
    146                 // Drain the queue
    147                 if(!__atomic_try_acquire(&ctx->cq.lock)) {
    148                         __STATS__( false, io.calls.locked++; )
    149                         return false;
    150                 }
    151 
    152                 return true;
    153         }
    154 
    155         static bool __cfa_do_drain( $io_context * ctx, cluster * cltr ) __attribute__((nonnull(1, 2))) {
    156                 /* paranoid */ verify( ! __preemption_enabled() );
    157                 /* paranoid */ verify( ready_schedule_islocked() );
    158                 /* paranoid */ verify( ctx->cq.lock == true );
    159 
    160                 const __u32 mask = *ctx->cq.mask;
    161                 unsigned long long ts_prev = ctx->cq.ts;
    162 
    163                 // re-read the head and tail in case it already changed.
    164                 const __u32 head = *ctx->cq.head;
    165                 const __u32 tail = *ctx->cq.tail;
    166                 const __u32 count = tail - head;
    167                 __STATS__( false, io.calls.drain++; io.calls.completed += count; )
    168 
    169                 for(i; count) {
    170                         unsigned idx = (head + i) & mask;
    171                         volatile struct io_uring_cqe & cqe = ctx->cq.cqes[idx];
    172 
    173                         /* paranoid */ verify(&cqe);
    174 
    175                         struct io_future_t * future = (struct io_future_t *)(uintptr_t)cqe.user_data;
    176                         // __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future );
    177 
    178                         __kernel_unpark( fulfil( *future, cqe.res, false ), UNPARK_LOCAL );
    179                 }
    180 
    181                 unsigned long long ts_next = ctx->cq.ts = rdtscl();
    182 
    183                 // Mark to the kernel that the cqe has been seen
    184                 // Ensure that the kernel only sees the new value of the head index after the CQEs have been read.
    185                 __atomic_store_n( ctx->cq.head, head + count, __ATOMIC_SEQ_CST );
    186                 ctx->proc->idle_wctx.drain_time = ts_next;
    187 
    188                 __cfadbg_print_safe(io, "Kernel I/O : %u completed age %llu\n", count, ts_next);
    189                 /* paranoid */ verify( ready_schedule_islocked() );
    190                 /* paranoid */ verify( ! __preemption_enabled() );
    191 
    192                 __atomic_unlock(&ctx->cq.lock);
    193 
    194                 touch_tsc( cltr->sched.io.tscs, ctx->cq.id, ts_prev, ts_next );
    195 
    196                 return true;
    197         }
    198 
    199         bool __cfa_io_drain( processor * proc ) {
    200                 bool local = false;
    201                 bool remote = false;
     162
     163                        __cfadbg_print_safe(io, "Kernel I/O : %u submitted to io_uring %d\n", ret, ctx.fd);
     164                        __STATS__( true, io.calls.submitted += ret; )
     165                        /* paranoid */ verify( ctx.sq.to_submit <= *ctx.sq.num );
     166                        /* paranoid */ verify( ctx.sq.to_submit >= ret );
     167
     168                        ctx.sq.to_submit -= ret;
     169
     170                        /* paranoid */ verify( ctx.sq.to_submit <= *ctx.sq.num );
     171
     172                        // Release the consumed SQEs
     173                        __release_sqes( ctx );
     174
     175                        /* paranoid */ verify( ! __preemption_enabled() );
     176
     177                        ctx.proc->io.pending = false;
     178                }
    202179
    203180                ready_schedule_lock();
    204 
    205                 cluster * const cltr = proc->cltr;
    206                 $io_context * const ctx = proc->io.ctx;
    207                 /* paranoid */ verify( cltr );
    208                 /* paranoid */ verify( ctx );
    209 
    210                 with(cltr->sched) {
    211                         const size_t ctxs_count = io.count;
    212 
    213                         /* paranoid */ verify( ready_schedule_islocked() );
    214                         /* paranoid */ verify( ! __preemption_enabled() );
    215                         /* paranoid */ verify( active_processor() == proc );
    216                         /* paranoid */ verify( __shard_factor.io > 0 );
    217                         /* paranoid */ verify( ctxs_count > 0 );
    218                         /* paranoid */ verify( ctx->cq.id < ctxs_count );
    219 
    220                         const unsigned this_cache = cache_id(cltr, ctx->cq.id / __shard_factor.io);
    221                         const unsigned long long ctsc = rdtscl();
    222 
    223                         if(proc->io.target == MAX) {
    224                                 uint64_t chaos = __tls_rand();
    225                                 unsigned ext = chaos & 0xff;
    226                                 unsigned other  = (chaos >> 8) % (ctxs_count);
    227 
    228                                 if(ext < 3 || __atomic_load_n(&caches[other / __shard_factor.io].id, __ATOMIC_RELAXED) == this_cache) {
    229                                         proc->io.target = other;
    230                                 }
    231                         }
    232                         else {
    233                                 const unsigned target = proc->io.target;
    234                                 /* paranoid */ verify( io.tscs[target].tv != MAX );
    235                                 HELP: if(target < ctxs_count) {
    236                                         const unsigned long long cutoff = calc_cutoff(ctsc, ctx->cq.id, ctxs_count, io.data, io.tscs, __shard_factor.io);
    237                                         const unsigned long long age = moving_average(ctsc, io.tscs[target].tv, io.tscs[target].ma);
    238                                         __cfadbg_print_safe(io, "Kernel I/O: Help attempt on %u from %u, age %'llu vs cutoff %'llu, %s\n", target, ctx->cq.id, age, cutoff, age > cutoff ? "yes" : "no");
    239                                         if(age <= cutoff) break HELP;
    240 
    241                                         if(!try_acquire(io.data[target])) break HELP;
    242 
    243                                         if(!__cfa_do_drain( io.data[target], cltr )) break HELP;
    244 
    245                                         remote = true;
    246                                         __STATS__( false, io.calls.helped++; )
    247                                 }
    248                                 proc->io.target = MAX;
    249                         }
    250                 }
    251 
    252 
    253                 // Drain the local queue
    254                 if(try_acquire( proc->io.ctx )) {
    255                         local = __cfa_do_drain( proc->io.ctx, cltr );
    256                 }
    257 
    258                 /* paranoid */ verify( ready_schedule_islocked() );
    259                 /* paranoid */ verify( ! __preemption_enabled() );
    260                 /* paranoid */ verify( active_processor() == proc );
    261 
     181                bool ret = __cfa_io_drain( proc );
    262182                ready_schedule_unlock();
    263                 return local || remote;
    264         }
    265 
    266         bool __cfa_io_flush( processor * proc ) {
    267                 /* paranoid */ verify( ! __preemption_enabled() );
    268                 /* paranoid */ verify( proc );
    269                 /* paranoid */ verify( proc->io.ctx );
    270 
    271                 $io_context & ctx = *proc->io.ctx;
    272 
    273                 __ioarbiter_flush( ctx );
    274 
    275                 if(ctx.sq.to_submit != 0) {
    276                         ioring_syscsll(ctx, 0, 0);
    277 
    278                 }
    279 
    280                 return __cfa_io_drain( proc );
     183                return ret;
    281184        }
    282185
     
    306209                struct io_uring_sqe * sqes = ctx->sq.sqes;
    307210                for(i; want) {
    308                         // __cfadbg_print_safe(io, "Kernel I/O : filling loop\n");
     211                        __cfadbg_print_safe(io, "Kernel I/O : filling loop\n");
    309212                        out_sqes[i] = &sqes[idxs[i]];
    310213                }
     
    324227                // copy all the indexes we want from the available list
    325228                for(i; want) {
    326                         // __cfadbg_print_safe(io, "Kernel I/O : allocating loop\n");
     229                        __cfadbg_print_safe(io, "Kernel I/O : allocating loop\n");
    327230                        idxs[i] = sq.free_ring.array[(fhead + i) & mask];
    328231                }
     
    341244        // sqe == &sqes[idx]
    342245        struct $io_context * cfa_io_allocate(struct io_uring_sqe * sqes[], __u32 idxs[], __u32 want) {
    343                 // __cfadbg_print_safe(io, "Kernel I/O : attempting to allocate %u\n", want);
     246                __cfadbg_print_safe(io, "Kernel I/O : attempting to allocate %u\n", want);
    344247
    345248                disable_interrupts();
     
    349252                /* paranoid */ verify( ctx );
    350253
    351                 // __cfadbg_print_safe(io, "Kernel I/O : attempting to fast allocation\n");
     254                __cfadbg_print_safe(io, "Kernel I/O : attempting to fast allocation\n");
    352255
    353256                // We can proceed to the fast path
     
    357260                        enable_interrupts();
    358261
    359                         // __cfadbg_print_safe(io, "Kernel I/O : fast allocation successful from ring %d\n", ctx->fd);
     262                        __cfadbg_print_safe(io, "Kernel I/O : fast allocation successful from ring %d\n", ctx->fd);
    360263
    361264                        __fill( sqes, want, idxs, ctx );
     
    372275                /* paranoid */ verify( ioarb );
    373276
    374                 // __cfadbg_print_safe(io, "Kernel I/O : falling back on arbiter for allocation\n");
     277                __cfadbg_print_safe(io, "Kernel I/O : falling back on arbiter for allocation\n");
    375278
    376279                struct $io_context * ret = __ioarbiter_allocate(*ioarb, idxs, want);
    377280
    378                 // __cfadbg_print_safe(io, "Kernel I/O : slow allocation completed from ring %d\n", ret->fd);
     281                __cfadbg_print_safe(io, "Kernel I/O : slow allocation completed from ring %d\n", ret->fd);
    379282
    380283                __fill( sqes, want, idxs,ret );
     
    384287        //=============================================================================================
    385288        // submission
    386         static inline void __submit_only( struct $io_context * ctx, __u32 idxs[], __u32 have) {
     289        static inline void __submit( struct $io_context * ctx, __u32 idxs[], __u32 have, bool lazy) {
    387290                // We can proceed to the fast path
    388291                // Get the right objects
     
    393296                // Add the sqes to the array
    394297                for( i; have ) {
    395                         // __cfadbg_print_safe(io, "Kernel I/O : __submit loop\n");
     298                        __cfadbg_print_safe(io, "Kernel I/O : __submit loop\n");
    396299                        sq.kring.array[ (tail + i) & mask ] = idxs[i];
    397300                }
     
    401304                sq.to_submit += have;
    402305
    403                 __atomic_store_n(&ctx->proc->io.pending, true, __ATOMIC_RELAXED);
    404                 __atomic_store_n(&ctx->proc->io.dirty  , true, __ATOMIC_RELAXED);
    405         }
    406 
    407         static inline void __submit( struct $io_context * ctx, __u32 idxs[], __u32 have, bool lazy) {
    408                 __sub_ring_t & sq = ctx->sq;
    409                 __submit_only(ctx, idxs, have);
    410 
     306                ctx->proc->io.pending = true;
     307                ctx->proc->io.dirty   = true;
    411308                if(sq.to_submit > 30) {
    412309                        __tls_stats()->io.flush.full++;
    413                         __cfa_io_flush( ctx->proc );
     310                        __cfa_io_flush( ctx->proc, 0 );
    414311                }
    415312                if(!lazy) {
    416313                        __tls_stats()->io.flush.eager++;
    417                         __cfa_io_flush( ctx->proc );
     314                        __cfa_io_flush( ctx->proc, 0 );
    418315                }
    419316        }
    420317
    421318        void cfa_io_submit( struct $io_context * inctx, __u32 idxs[], __u32 have, bool lazy ) __attribute__((nonnull (1))) {
    422                 // __cfadbg_print_safe(io, "Kernel I/O : attempting to submit %u (%s)\n", have, lazy ? "lazy" : "eager");
     319                __cfadbg_print_safe(io, "Kernel I/O : attempting to submit %u (%s)\n", have, lazy ? "lazy" : "eager");
    423320
    424321                disable_interrupts();
     
    437334                        enable_interrupts();
    438335
    439                         // __cfadbg_print_safe(io, "Kernel I/O : submitted on fast path\n");
     336                        __cfadbg_print_safe(io, "Kernel I/O : submitted on fast path\n");
    440337                        return;
    441338                }
     
    445342                enable_interrupts();
    446343
    447                 // __cfadbg_print_safe(io, "Kernel I/O : falling back on arbiter for submission\n");
     344                __cfadbg_print_safe(io, "Kernel I/O : falling back on arbiter for submission\n");
    448345
    449346                __ioarbiter_submit(inctx, idxs, have, lazy);
     
    489386                // go through the range and release the sqes
    490387                for( i; count ) {
    491                         // __cfadbg_print_safe(io, "Kernel I/O : release loop\n");
     388                        __cfadbg_print_safe(io, "Kernel I/O : release loop\n");
    492389                        __u32 idx = ctx.sq.kring.array[ (phead + i) & mask ];
    493390                        ctx.sq.free_ring.array[ (ftail + i) & mask ] = idx;
     
    505402// I/O Arbiter
    506403//=============================================================================================
    507         static inline bool enqueue(__outstanding_io_queue & queue, __outstanding_io & item) {
    508                 bool was_empty;
    509 
     404        static inline void block(__outstanding_io_queue & queue, __outstanding_io & item) {
    510405                // Lock the list, it's not thread safe
    511406                lock( queue.lock __cfaabi_dbg_ctx2 );
    512407                {
    513                         was_empty = empty(queue.queue);
    514 
    515408                        // Add our request to the list
    516409                        add( queue.queue, item );
     
    521414                unlock( queue.lock );
    522415
    523                 return was_empty;
     416                wait( item.sem );
    524417        }
    525418
     
    529422
    530423        static $io_context * __ioarbiter_allocate( $io_arbiter & this, __u32 idxs[], __u32 want ) {
    531                 // __cfadbg_print_safe(io, "Kernel I/O : arbiter allocating\n");
     424                __cfadbg_print_safe(io, "Kernel I/O : arbiter allocating\n");
    532425
    533426                __STATS__( false, io.alloc.block += 1; )
     
    539432                pa.want = want;
    540433
    541                 enqueue(this.pending, (__outstanding_io&)pa);
    542 
    543                 wait( pa.sem );
     434                block(this.pending, (__outstanding_io&)pa);
    544435
    545436                return pa.ctx;
     
    594485                ei.lazy = lazy;
    595486
    596                 bool we = enqueue(ctx->ext_sq, (__outstanding_io&)ei);
    597 
    598                 __atomic_store_n(&ctx->proc->io.pending, true, __ATOMIC_SEQ_CST);
    599 
    600                 if( we ) {
    601                         sigval_t value = { PREEMPT_IO };
    602                         pthread_sigqueue(ctx->proc->kernel_thread, SIGUSR1, value);
    603                 }
    604 
    605                 wait( ei.sem );
     487                block(ctx->ext_sq, (__outstanding_io&)ei);
    606488
    607489                __cfadbg_print_safe(io, "Kernel I/O : %u submitted from arbiter\n", have);
     
    619501                                        __external_io & ei = (__external_io&)drop( ctx.ext_sq.queue );
    620502
    621                                         __submit_only(&ctx, ei.idxs, ei.have);
     503                                        __submit(&ctx, ei.idxs, ei.have, ei.lazy);
    622504
    623505                                        post( ei.sem );
     
    641523
    642524                        // We can proceed to the fast path
    643                         if( !__alloc(ctx, &idx, 1) ) {
    644                                 /* paranoid */ verify( false ); // for now check if this happens, next time just abort the sleep.
    645                                 return false;
    646                         }
     525                        if( !__alloc(ctx, &idx, 1) ) return false;
    647526
    648527                        // Allocation was successful
     
    674553
    675554                        /* paranoid */ verify( sqe->user_data == (uintptr_t)&future );
    676                         __submit_only( ctx, &idx, 1 );
     555                        __submit( ctx, &idx, 1, true );
    677556
    678557                        /* paranoid */ verify( proc == __cfaabi_tls.this_processor );
     
    681560                        return true;
    682561                }
    683 
    684                 void __cfa_io_idle( processor * proc ) {
    685                         iovec iov;
    686                         __atomic_acquire( &proc->io.ctx->cq.lock );
    687 
    688                         __attribute__((used)) volatile bool was_reset = false;
    689 
    690                         with( proc->idle_wctx) {
    691 
    692                                 // Do we already have a pending read
    693                                 if(available(*ftr)) {
    694                                         // There is no pending read, we need to add one
    695                                         reset(*ftr);
    696 
    697                                         iov.iov_base = rdbuf;
    698                                         iov.iov_len  = sizeof(eventfd_t);
    699                                         __kernel_read(proc, *ftr, iov, evfd );
    700                                         ftr->result = 0xDEADDEAD;
    701                                         *((eventfd_t *)rdbuf) = 0xDEADDEADDEADDEAD;
    702                                         was_reset = true;
    703                                 }
    704                         }
    705 
    706                         if( !__atomic_load_n( &proc->do_terminate, __ATOMIC_SEQ_CST ) ) {
    707                                 __ioarbiter_flush( *proc->io.ctx );
    708                                 proc->idle_wctx.sleep_time = rdtscl();
    709                                 ioring_syscsll( *proc->io.ctx, 1, IORING_ENTER_GETEVENTS);
    710                         }
    711 
    712                         ready_schedule_lock();
    713                         __cfa_do_drain( proc->io.ctx, proc->cltr );
    714                         ready_schedule_unlock();
    715 
    716                         asm volatile ("" :: "m" (was_reset));
    717                 }
    718562        #endif
    719563#endif
  • libcfa/src/concurrency/io/setup.cfa

    r92538ab r4559b34  
    3232
    3333        void __cfa_io_start( processor * proc ) {}
    34         bool __cfa_io_flush( processor * proc ) { return false; }
    35         bool __cfa_io_drain( processor * proc ) __attribute__((nonnull (1))) { return false; }
    36         void __cfa_io_idle ( processor * ) __attribute__((nonnull (1))) {}
     34        bool __cfa_io_flush( processor * proc, int ) { return false; }
    3735        void __cfa_io_stop ( processor * proc ) {}
    3836
     
    4139
    4240#else
    43 #pragma GCC diagnostic push
    44 #pragma GCC diagnostic ignored "-Waddress-of-packed-member"
    4541        #include <errno.h>
    4642        #include <stdint.h>
     
    6056
    6157        #include "bitmanip.hfa"
    62         #include "fstream.hfa"
    63         #include "kernel/private.hfa"
    64         #include "limits.hfa"
     58        #include "kernel_private.hfa"
    6559        #include "thread.hfa"
    66 #pragma GCC diagnostic pop
    6760
    6861        void ?{}(io_context_params & this) {
     
    118111                this.ext_sq.empty = true;
    119112                (this.ext_sq.queue){};
    120                 __io_uring_setup( this, cl.io.params, proc->idle_wctx.evfd );
     113                __io_uring_setup( this, cl.io.params, proc->idle_fd );
    121114                __cfadbg_print_safe(io_core, "Kernel I/O : Created ring for io_context %u (%p)\n", this.fd, &this);
    122115        }
     
    128121                __cfadbg_print_safe(io_core, "Kernel I/O : Destroyed ring for io_context %u\n", this.fd);
    129122        }
     123
     124        extern void __disable_interrupts_hard();
     125        extern void __enable_interrupts_hard();
    130126
    131127        static void __io_uring_setup( $io_context & this, const io_context_params & params_in, int procfd ) {
     
    217213
    218214                // completion queue
    219                 cq.lock      = false;
    220                 cq.id        = MAX;
    221                 cq.ts        = rdtscl();
    222215                cq.head      = (volatile __u32 *)(((intptr_t)cq.ring_ptr) + params.cq_off.head);
    223216                cq.tail      = (volatile __u32 *)(((intptr_t)cq.ring_ptr) + params.cq_off.tail);
     
    233226                        __cfadbg_print_safe(io_core, "Kernel I/O : registering %d for completion with ring %d\n", procfd, fd);
    234227
     228                        __disable_interrupts_hard();
     229
    235230                        int ret = syscall( __NR_io_uring_register, fd, IORING_REGISTER_EVENTFD, &procfd, 1);
    236231                        if (ret < 0) {
    237232                                abort("KERNEL ERROR: IO_URING EVENTFD REGISTER - %s\n", strerror(errno));
    238233                        }
     234
     235                        __enable_interrupts_hard();
    239236
    240237                        __cfadbg_print_safe(io_core, "Kernel I/O : registered %d for completion with ring %d\n", procfd, fd);
     
    261258                struct __sub_ring_t & sq = this.sq;
    262259                struct __cmp_ring_t & cq = this.cq;
    263                 {
    264                         __u32 fhead = sq.free_ring.head;
    265                         __u32 ftail = sq.free_ring.tail;
    266 
    267                         __u32 total = *sq.num;
    268                         __u32 avail = ftail - fhead;
    269 
    270                         if(avail != total) abort | "Processor (" | (void*)this.proc | ") tearing down ring with" | (total - avail) | "entries allocated but not submitted, out of" | total;
    271                 }
    272260
    273261                // unmap the submit queue entries
  • libcfa/src/concurrency/io/types.hfa

    r92538ab r4559b34  
    2323#include "bits/locks.hfa"
    2424#include "bits/queue.hfa"
    25 #include "iofwd.hfa"
    2625#include "kernel/fwd.hfa"
    27 #include "limits.hfa"
    2826
    2927#if defined(CFA_HAVE_LINUX_IO_URING_H)
     
    7977
    8078        struct __cmp_ring_t {
    81                 volatile bool lock;
    82 
    83                 unsigned id;
    84 
    85                 unsigned long long ts;
    86 
    8779                // Head and tail of the ring
    8880                volatile __u32 * head;
     
    136128        };
    137129
    138         static inline unsigned long long ts($io_context *& this) {
    139                 const __u32 head = *this->cq.head;
    140                 const __u32 tail = *this->cq.tail;
    141 
    142                 if(head == tail) return MAX;
    143 
    144                 return this->cq.ts;
    145         }
    146 
    147130        struct __pending_alloc {
    148131                inline __outstanding_io;
     
    187170        // void __ioctx_prepare_block($io_context & ctx);
    188171#endif
     172
     173//-----------------------------------------------------------------------
     174// IO user data
     175struct io_future_t {
     176        future_t self;
     177        __s32 result;
     178};
     179
     180static inline {
     181        thread$ * fulfil( io_future_t & this, __s32 result, bool do_unpark = true ) {
     182                this.result = result;
     183                return fulfil(this.self, do_unpark);
     184        }
     185
     186        // Wait for the future to be fulfilled
     187        bool wait     ( io_future_t & this ) { return wait     (this.self); }
     188        void reset    ( io_future_t & this ) { return reset    (this.self); }
     189        bool available( io_future_t & this ) { return available(this.self); }
     190}
  • libcfa/src/concurrency/iofwd.hfa

    r92538ab r4559b34  
    1919extern "C" {
    2020        #include <asm/types.h>
    21         #include <sys/stat.h> // needed for mode_t
    2221        #if CFA_HAVE_LINUX_IO_URING_H
    2322                #include <linux/io_uring.h>
     
    2524}
    2625#include "bits/defs.hfa"
    27 #include "kernel/fwd.hfa"
    2826#include "time.hfa"
    2927
     
    4947
    5048struct cluster;
     49struct io_future_t;
    5150struct $io_context;
    5251
     
    5857
    5958struct io_uring_sqe;
    60 
    61 //-----------------------------------------------------------------------
    62 // IO user data
    63 struct io_future_t {
    64         future_t self;
    65         __s32 result;
    66 };
    67 
    68 static inline {
    69         thread$ * fulfil( io_future_t & this, __s32 result, bool do_unpark = true ) {
    70                 this.result = result;
    71                 return fulfil(this.self, do_unpark);
    72         }
    73 
    74         // Wait for the future to be fulfilled
    75         bool wait     ( io_future_t & this ) { return wait     (this.self); }
    76         void reset    ( io_future_t & this ) { return reset    (this.self); }
    77         bool available( io_future_t & this ) { return available(this.self); }
    78 }
    7959
    8060//----------
     
    153133// Check if a function is blocks a only the user thread
    154134bool has_user_level_blocking( fptr_t func );
    155 
    156 #if CFA_HAVE_LINUX_IO_URING_H
    157         static inline void zero_sqe(struct io_uring_sqe * sqe) {
    158                 sqe->flags = 0;
    159                 sqe->ioprio = 0;
    160                 sqe->fd = 0;
    161                 sqe->off = 0;
    162                 sqe->addr = 0;
    163                 sqe->len = 0;
    164                 sqe->fsync_flags = 0;
    165                 sqe->__pad2[0] = 0;
    166                 sqe->__pad2[1] = 0;
    167                 sqe->__pad2[2] = 0;
    168                 sqe->fd = 0;
    169                 sqe->off = 0;
    170                 sqe->addr = 0;
    171                 sqe->len = 0;
    172         }
    173 #endif
  • libcfa/src/concurrency/kernel.cfa

    r92538ab r4559b34  
    1919// #define __CFA_DEBUG_PRINT_RUNTIME_CORE__
    2020
    21 #pragma GCC diagnostic push
    22 #pragma GCC diagnostic ignored "-Waddress-of-packed-member"
    23 
    2421//C Includes
    2522#include <errno.h>
     
    2825#include <signal.h>
    2926#include <unistd.h>
    30 
    3127extern "C" {
    3228        #include <sys/eventfd.h>
     
    3531
    3632//CFA Includes
    37 #include "kernel/private.hfa"
     33#include "kernel_private.hfa"
    3834#include "preemption.hfa"
    3935#include "strstream.hfa"
     
    4440#define __CFA_INVOKE_PRIVATE__
    4541#include "invoke.h"
    46 #pragma GCC diagnostic pop
    4742
    4843#if !defined(__CFA_NO_STATISTICS__)
     
    132127static void __wake_one(cluster * cltr);
    133128
    134 static void idle_sleep(processor * proc);
     129static void idle_sleep(processor * proc, io_future_t & future, iovec & iov);
    135130static bool mark_idle (__cluster_proc_list & idles, processor & proc);
    136131static void mark_awake(__cluster_proc_list & idles, processor & proc);
    137132
    138 extern bool __cfa_io_drain( processor * proc ) __attribute__((nonnull (1)));
    139 extern bool __cfa_io_flush( processor * ) __attribute__((nonnull (1)));
    140 extern void __cfa_io_idle( processor * ) __attribute__((nonnull (1)));
     133extern void __cfa_io_start( processor * );
     134extern bool __cfa_io_drain( processor * );
     135extern bool __cfa_io_flush( processor *, int min_comp );
     136extern void __cfa_io_stop ( processor * );
     137static inline bool __maybe_io_drain( processor * );
    141138
    142139#if defined(CFA_WITH_IO_URING_IDLE)
     
    162159        verify(this);
    163160
    164         /* paranoid */ verify( this->idle_wctx.ftr   != 0p );
    165         /* paranoid */ verify( this->idle_wctx.rdbuf != 0p );
    166 
    167         // used for idle sleep when io_uring is present
    168         // mark it as already fulfilled so we know if there is a pending request or not
    169         this->idle_wctx.ftr->self.ptr = 1p;
     161        io_future_t future; // used for idle sleep when io_uring is present
     162        future.self.ptr = 1p;  // mark it as already fulfilled so we know if there is a pending request or not
     163        eventfd_t idle_val;
     164        iovec idle_iovec = { &idle_val, sizeof(idle_val) };
     165
     166        __cfa_io_start( this );
    170167
    171168        __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this);
     
    192189                for() {
    193190                        // Check if there is pending io
    194                         __cfa_io_drain( this );
     191                        __maybe_io_drain( this );
    195192
    196193                        // Try to get the next thread
     
    198195
    199196                        if( !readyThread ) {
    200                                 // there is no point in holding submissions if we are idle
    201197                                __IO_STATS__(true, io.flush.idle++; )
    202                                 __cfa_io_flush( this );
    203 
    204                                 // drain again in case something showed up
    205                                 __cfa_io_drain( this );
     198                                __cfa_io_flush( this, 0 );
    206199
    207200                                readyThread = __next_thread( this->cltr );
     
    209202
    210203                        if( !readyThread ) for(5) {
     204                                __IO_STATS__(true, io.flush.idle++; )
     205
    211206                                readyThread = __next_thread_slow( this->cltr );
    212207
    213208                                if( readyThread ) break;
    214209
    215                                 // It's unlikely we still I/O to submit, but the arbiter could
    216                                 __IO_STATS__(true, io.flush.idle++; )
    217                                 __cfa_io_flush( this );
    218 
    219                                 // drain again in case something showed up
    220                                 __cfa_io_drain( this );
     210                                __cfa_io_flush( this, 0 );
    221211                        }
    222212
     
    241231                                }
    242232
    243                                 idle_sleep( this );
     233                                idle_sleep( this, future, idle_iovec );
    244234
    245235                                // We were woken up, remove self from idle
     
    261251                        if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
    262252
    263                         if(__atomic_load_n(&this->io.pending, __ATOMIC_RELAXED) && !__atomic_load_n(&this->io.dirty, __ATOMIC_RELAXED)) {
     253                        if(this->io.pending && !this->io.dirty) {
    264254                                __IO_STATS__(true, io.flush.dirty++; )
    265                                 __cfa_io_flush( this );
     255                                __cfa_io_flush( this, 0 );
    266256                        }
    267257                }
     
    269259                __cfadbg_print_safe(runtime_core, "Kernel : core %p stopping\n", this);
    270260        }
     261
     262        for(int i = 0; !available(future); i++) {
     263                if(i > 1000) __cfaabi_dbg_write( "ERROR: kernel has bin spinning on a flush after exit loop.\n", 60);
     264                __cfa_io_flush( this, 1 );
     265        }
     266
     267        __cfa_io_stop( this );
    271268
    272269        post( this->terminated );
     
    637634
    638635        int fd = 1;
    639         if( __atomic_load_n(&fdp->sem, __ATOMIC_SEQ_CST) != 1 ) {
    640                 fd = __atomic_exchange_n(&fdp->sem, 1, __ATOMIC_RELAXED);
     636        if( __atomic_load_n(&fdp->fd, __ATOMIC_SEQ_CST) != 1 ) {
     637                fd = __atomic_exchange_n(&fdp->fd, 1, __ATOMIC_RELAXED);
    641638        }
    642639
    643640        switch(fd) {
    644                 __attribute__((unused)) int ret;
    645641        case 0:
    646642                // If the processor isn't ready to sleep then the exchange will already wake it up
     
    660656                // If the processor was ready to sleep, we need to wake it up with an actual write
    661657                val = 1;
    662                 ret = eventfd_write( fd, val );
    663                 /* paranoid */ verifyf( ret == 0, "Expected return to be 0, was %d\n", ret );
     658                eventfd_write( fd, val );
    664659
    665660                #if !defined(__CFA_NO_STATISTICS__)
     
    682677        __cfadbg_print_safe(runtime_core, "Kernel : waking Processor %p\n", this);
    683678
    684         this->idle_wctx.sem = 1;
    685 
    686         this->idle_wctx.wake__time = rdtscl();
     679        this->idle_wctx.fd = 1;
    687680
    688681        eventfd_t val;
    689682        val = 1;
    690         __attribute__((unused)) int ret = eventfd_write( this->idle_wctx.evfd, val );
    691 
    692         /* paranoid */ verifyf( ret == 0, "Expected return to be 0, was %d\n", ret );
    693         /* paranoid */ verify( ! __preemption_enabled() );
    694 }
    695 
    696 static void idle_sleep(processor * this) {
    697         /* paranoid */ verify( this->idle_wctx.evfd != 1 );
    698         /* paranoid */ verify( this->idle_wctx.evfd != 2 );
    699 
     683        eventfd_write( this->idle_fd, val );
     684
     685        /* paranoid */ verify( ! __preemption_enabled() );
     686}
     687
     688static void idle_sleep(processor * this, io_future_t & future, iovec & iov) {
    700689        // Tell everyone we are ready to go do sleep
    701690        for() {
    702                 int expected = this->idle_wctx.sem;
     691                int expected = this->idle_wctx.fd;
    703692
    704693                // Someone already told us to wake-up! No time for a nap.
     
    706695
    707696                // Try to mark that we are going to sleep
    708                 if(__atomic_compare_exchange_n(&this->idle_wctx.sem, &expected, this->idle_wctx.evfd, false,  __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) {
     697                if(__atomic_compare_exchange_n(&this->idle_wctx.fd, &expected, this->idle_fd, false,  __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) {
    709698                        // Every one agreed, taking a nap
    710699                        break;
     
    724713                {
    725714                        eventfd_t val;
    726                         ssize_t ret = read( this->idle_wctx.evfd, &val, sizeof(val) );
     715                        ssize_t ret = read( this->idle_fd, &val, sizeof(val) );
    727716                        if(ret < 0) {
    728717                                switch((int)errno) {
     
    746735                #endif
    747736        #else
    748                 __cfa_io_idle( this );
     737                // Do we already have a pending read
     738                if(available(future)) {
     739                        // There is no pending read, we need to add one
     740                        reset(future);
     741
     742                        __kernel_read(this, future, iov, this->idle_fd );
     743                }
     744
     745                __cfa_io_flush( this, 1 );
    749746        #endif
    750747}
     
    753750        __STATS__(true, ready.sleep.halts++; )
    754751
    755         proc.idle_wctx.sem = 0;
     752        proc.idle_wctx.fd = 0;
    756753
    757754        /* paranoid */ verify( ! __preemption_enabled() );
     
    834831#endif
    835832
    836 
     833static inline bool __maybe_io_drain( processor * proc ) {
     834        bool ret = false;
     835        #if defined(CFA_HAVE_LINUX_IO_URING_H)
     836                __cfadbg_print_safe(runtime_core, "Kernel : core %p checking io for ring %d\n", proc, proc->io.ctx->fd);
     837
     838                // Check if we should drain the queue
     839                $io_context * ctx = proc->io.ctx;
     840                unsigned head = *ctx->cq.head;
     841                unsigned tail = *ctx->cq.tail;
     842                if(head == tail) return false;
     843                ready_schedule_lock();
     844                ret = __cfa_io_drain( proc );
     845                ready_schedule_unlock();
     846        #endif
     847        return ret;
     848}
    837849
    838850//-----------------------------------------------------------------------------
     
    891903        void print_stats_now( cluster & this, int flags ) {
    892904                crawl_cluster_stats( this );
    893                 __print_stats( this.stats, flags, "Cluster", this.name, (void*)&this );
     905                __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this );
    894906        }
    895907#endif
  • libcfa/src/concurrency/kernel.hfa

    r92538ab r4559b34  
    4848extern struct cluster * mainCluster;
    4949
    50 // Coroutine used py processors for the 2-step context switch
     50// Processor id, required for scheduling threads
     51
     52
    5153coroutine processorCtx_t {
    5254        struct processor * proc;
    5355};
    5456
    55 struct io_future_t;
    56 
    57 // Information needed for idle sleep
     57
    5858struct __fd_waitctx {
    59         // semaphore/future like object
    60         // values can be 0, 1 or some file descriptor.
    61         // 0 - is the default state
    62         // 1 - means the proc should wake-up immediately
    63         // FD - means the proc is going asleep and should be woken by writing to the FD.
    64         volatile int sem;
    65 
    66         // The event FD that corresponds to this processor
    67         int evfd;
    68 
    69         // buffer into which the proc will read from evfd
    70         // unused if not using io_uring for idle sleep
    71         void * rdbuf;
    72 
    73         // future use to track the read of the eventfd
    74         // unused if not using io_uring for idle sleep
    75         io_future_t * ftr;
    76 
    77         volatile unsigned long long wake__time;
    78         volatile unsigned long long sleep_time;
    79         volatile unsigned long long drain_time;
     59        volatile int fd;
    8060};
    8161
     
    11292        struct {
    11393                $io_context * ctx;
    114                 unsigned target;
    115                 volatile bool pending;
    116                 volatile bool dirty;
     94                bool pending;
     95                bool dirty;
    11796        } io;
    11897
     
    124103        bool pending_preemption;
    125104
    126         // context for idle sleep
     105        // Idle lock (kernel semaphore)
     106        int idle_fd;
     107
     108        // Idle waitctx
    127109        struct __fd_waitctx idle_wctx;
    128110
     
    173155void ^?{}(__intrusive_lane_t & this);
    174156
    175 // Aligned timestamps which are used by the ready queue and io subsystem
     157// Aligned timestamps which are used by the relaxed ready queue
    176158struct __attribute__((aligned(128))) __timestamp_t {
    177159        volatile unsigned long long tv;
     
    179161};
    180162
     163struct __attribute__((aligned(16))) __cache_id_t {
     164        volatile unsigned id;
     165};
     166
     167// Aligned timestamps which are used by the relaxed ready queue
     168struct __attribute__((aligned(128))) __help_cnts_t {
     169        volatile unsigned long long src;
     170        volatile unsigned long long dst;
     171        volatile unsigned long long tri;
     172};
     173
    181174static inline void  ?{}(__timestamp_t & this) { this.tv = 0; this.ma = 0; }
    182175static inline void ^?{}(__timestamp_t &) {}
    183176
    184 
    185 struct __attribute__((aligned(16))) __cache_id_t {
    186         volatile unsigned id;
    187 };
     177struct __attribute__((aligned(128))) __ready_queue_caches_t;
     178void  ?{}(__ready_queue_caches_t & this);
     179void ^?{}(__ready_queue_caches_t & this);
     180
     181//TODO adjust cache size to ARCHITECTURE
     182// Structure holding the ready queue
     183struct __ready_queue_t {
     184        // Data tracking the actual lanes
     185        // On a seperate cacheline from the used struct since
     186        // used can change on each push/pop but this data
     187        // only changes on shrink/grow
     188        struct {
     189                // Arary of lanes
     190                __intrusive_lane_t * volatile data;
     191
     192                // Array of times
     193                __timestamp_t * volatile tscs;
     194
     195                __cache_id_t * volatile caches;
     196
     197                // Array of stats
     198                __help_cnts_t * volatile help;
     199
     200                // Number of lanes (empty or not)
     201                volatile size_t count;
     202        } lanes;
     203};
     204
     205void  ?{}(__ready_queue_t & this);
     206void ^?{}(__ready_queue_t & this);
     207#if !defined(__CFA_NO_STATISTICS__)
     208        unsigned cnt(const __ready_queue_t & this, unsigned idx);
     209#endif
    188210
    189211// Idle Sleep
     
    211233// Cluster
    212234struct __attribute__((aligned(128))) cluster {
    213         struct {
    214                 struct {
    215                         // Arary of subqueues
    216                         __intrusive_lane_t * data;
    217 
    218                         // Time since subqueues were processed
    219                         __timestamp_t * tscs;
    220 
    221                         // Number of subqueue / timestamps
    222                         size_t count;
    223                 } readyQ;
    224 
    225                 struct {
    226                         // Array of $io_
    227                         $io_context ** data;
    228 
    229                         // Time since subqueues were processed
    230                         __timestamp_t * tscs;
    231 
    232                         // Number of I/O subqueues
    233                         size_t count;
    234                 } io;
    235 
    236                 // Cache each kernel thread belongs to
    237                 __cache_id_t * caches;
    238         } sched;
    239 
    240         // // Ready queue for threads
    241         // __ready_queue_t ready_queue;
     235        // Ready queue for threads
     236        __ready_queue_t ready_queue;
    242237
    243238        // Name of the cluster
  • libcfa/src/concurrency/kernel/fwd.hfa

    r92538ab r4559b34  
    248248                        // check if the future is available
    249249                        bool available( future_t & this ) {
    250                                 while( this.ptr == 2p ) Pause();
    251250                                return this.ptr == 1p;
    252251                        }
     
    348347                                        struct oneshot * want = expected == 0p ? 1p : 2p;
    349348                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
    350                                                 if( expected == 0p ) { return 0p; }
     349                                                if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return 0p; }
    351350                                                thread$ * ret = post( *expected, do_unpark );
    352351                                                __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
  • libcfa/src/concurrency/kernel/startup.cfa

    r92538ab r4559b34  
    1818
    1919// C Includes
    20 #include <errno.h>                                      // errno
     20#include <errno.h>              // errno
    2121#include <signal.h>
    22 #include <string.h>                                     // strerror
    23 #include <unistd.h>                                     // sysconf
     22#include <string.h>             // strerror
     23#include <unistd.h>             // sysconf
    2424
    2525extern "C" {
    26         #include <limits.h>                             // PTHREAD_STACK_MIN
    27         #include <unistd.h>                             // syscall
    28         #include <sys/eventfd.h>                        // eventfd
    29         #include <sys/mman.h>                           // mprotect
    30         #include <sys/resource.h>                       // getrlimit
     26      #include <limits.h>       // PTHREAD_STACK_MIN
     27        #include <unistd.h>       // syscall
     28        #include <sys/eventfd.h>  // eventfd
     29      #include <sys/mman.h>     // mprotect
     30      #include <sys/resource.h> // getrlimit
    3131}
    3232
    3333// CFA Includes
    34 #include "kernel/private.hfa"
    35 #include "iofwd.hfa"
    36 #include "startup.hfa"                                  // STARTUP_PRIORITY_XXX
     34#include "kernel_private.hfa"
     35#include "startup.hfa"          // STARTUP_PRIORITY_XXX
    3736#include "limits.hfa"
    3837#include "math.hfa"
     
    9897extern void __kernel_alarm_startup(void);
    9998extern void __kernel_alarm_shutdown(void);
    100 extern void __cfa_io_start( processor * );
    101 extern void __cfa_io_stop ( processor * );
    10299
    103100//-----------------------------------------------------------------------------
     
    105102extern void __wake_proc(processor *);
    106103extern int cfa_main_returned;                                                   // from interpose.cfa
    107 uint32_t __global_random_prime = 4_294_967_291u, __global_random_mask = false;
     104extern uint32_t __global_random_seed;
    108105
    109106//-----------------------------------------------------------------------------
     
    114111KERNEL_STORAGE(__stack_t,            mainThreadCtx);
    115112KERNEL_STORAGE(__scheduler_RWLock_t, __scheduler_lock);
    116 KERNEL_STORAGE(eventfd_t,            mainIdleEventFd);
    117 KERNEL_STORAGE(io_future_t,          mainIdleFuture);
    118113#if !defined(__CFA_NO_STATISTICS__)
    119114KERNEL_STORAGE(__stats_t, mainProcStats);
     
    229224        (*mainProcessor){};
    230225
    231         mainProcessor->idle_wctx.rdbuf = &storage_mainIdleEventFd;
    232         mainProcessor->idle_wctx.ftr   = (io_future_t*)&storage_mainIdleFuture;
    233         /* paranoid */ verify( sizeof(storage_mainIdleEventFd) == sizeof(eventfd_t) );
    234 
    235         __cfa_io_start( mainProcessor );
    236226        register_tls( mainProcessor );
    237227
     
    315305
    316306        unregister_tls( mainProcessor );
    317         __cfa_io_stop( mainProcessor );
    318307
    319308        // Destroy the main processor and its context in reverse order of construction
     
    364353        proc->local_data = &__cfaabi_tls;
    365354
    366         __cfa_io_start( proc );
    367355        register_tls( proc );
    368 
    369         // used for idle sleep when io_uring is present
    370         io_future_t future;
    371         eventfd_t idle_buf;
    372         proc->idle_wctx.ftr = &future;
    373         proc->idle_wctx.rdbuf = &idle_buf;
    374 
    375356
    376357        // SKULLDUGGERY: We want to create a context for the processor coroutine
     
    414395
    415396        unregister_tls( proc );
    416         __cfa_io_stop( proc );
    417397
    418398        return 0p;
     
    510490        preferred = ready_queue_new_preferred();
    511491        last_proc = 0p;
    512         random_state = __global_random_mask ? __global_random_prime : __global_random_prime ^ rdtscl();
     492        random_state = __global_random_seed;
    513493        #if defined( __CFA_WITH_VERIFY__ )
    514494                canary = 0x0D15EA5E0D15EA5Ep;
     
    535515        this.rdq.its = 0;
    536516        this.rdq.itr = 0;
    537         this.rdq.id  = 0;
     517        this.rdq.id  = MAX;
    538518        this.rdq.target = MAX;
    539519        this.rdq.last = MAX;
     
    552532        this.local_data = 0p;
    553533
    554         idle_wctx.evfd = eventfd(0, 0);
    555         if (idle_wctx.evfd < 0) {
     534        this.idle_fd = eventfd(0, 0);
     535        if (idle_fd < 0) {
    556536                abort("KERNEL ERROR: PROCESSOR EVENTFD - %s\n", strerror(errno));
    557537        }
    558538
    559         idle_wctx.sem = 0;
    560         idle_wctx.wake__time = 0;
     539        this.idle_wctx.fd = 0;
    561540
    562541        // I'm assuming these two are reserved for standard input and output
    563542        // so I'm using them as sentinels with idle_wctx.
    564         /* paranoid */ verify( idle_wctx.evfd != 0 );
    565         /* paranoid */ verify( idle_wctx.evfd != 1 );
     543        /* paranoid */ verify( this.idle_fd != 0 );
     544        /* paranoid */ verify( this.idle_fd != 1 );
    566545
    567546        #if !defined(__CFA_NO_STATISTICS__)
     
    575554// Not a ctor, it just preps the destruction but should not destroy members
    576555static void deinit(processor & this) {
    577         close(this.idle_wctx.evfd);
     556        close(this.idle_fd);
    578557}
    579558
     
    626605        this.name = name;
    627606        this.preemption_rate = preemption_rate;
    628         this.sched.readyQ.data = 0p;
    629         this.sched.readyQ.tscs = 0p;
    630         this.sched.readyQ.count = 0;
    631         this.sched.io.tscs = 0p;
    632         this.sched.io.data = 0p;
    633         this.sched.caches = 0p;
     607        ready_queue{};
    634608
    635609        #if !defined(__CFA_NO_STATISTICS__)
     
    670644        // Unlock the RWlock
    671645        ready_mutate_unlock( last_size );
    672 
    673         ready_queue_close( &this );
    674         /* paranoid */ verify( this.sched.readyQ.data == 0p );
    675         /* paranoid */ verify( this.sched.readyQ.tscs == 0p );
    676         /* paranoid */ verify( this.sched.readyQ.count == 0 );
    677         /* paranoid */ verify( this.sched.io.tscs == 0p );
    678         /* paranoid */ verify( this.sched.caches == 0p );
    679 
    680646        enable_interrupts( false ); // Don't poll, could be in main cluster
    681 
    682647
    683648        #if !defined(__CFA_NO_STATISTICS__)
     
    771736        check( pthread_attr_init( &attr ), "pthread_attr_init" ); // initialize attribute
    772737
    773         size_t stacksize = max( PTHREAD_STACK_MIN, DEFAULT_STACK_SIZE );
     738        size_t stacksize = DEFAULT_STACK_SIZE;
    774739
    775740        void * stack;
  • libcfa/src/concurrency/locks.cfa

    r92538ab r4559b34  
    1919
    2020#include "locks.hfa"
    21 #include "kernel/private.hfa"
     21#include "kernel_private.hfa"
    2222
    2323#include <kernel.hfa>
  • libcfa/src/concurrency/locks.hfa

    r92538ab r4559b34  
    164164}
    165165
    166 static inline void lock(linear_backoff_then_block_lock & this) with(this) {
     166static inline bool lock(linear_backoff_then_block_lock & this) with(this) {
    167167        // if owner just return
    168         if (active_thread() == owner) return;
     168        if (active_thread() == owner) return true;
    169169        size_t compare_val = 0;
    170170        int spin = spin_start;
     
    172172        for( ;; ) {
    173173                compare_val = 0;
    174                 if (internal_try_lock(this, compare_val)) return;
     174                if (internal_try_lock(this, compare_val)) return true;
    175175                if (2 == compare_val) break;
    176176                for (int i = 0; i < spin; i++) Pause();
     
    179179        }
    180180
    181         if(2 != compare_val && try_lock_contention(this)) return;
     181        if(2 != compare_val && try_lock_contention(this)) return true;
    182182        // block until signalled
    183         while (block(this)) if(try_lock_contention(this)) return;
     183        while (block(this)) if(try_lock_contention(this)) return true;
     184
     185        // this should never be reached as block(this) always returns true
     186        return false;
    184187}
    185188
  • libcfa/src/concurrency/monitor.cfa

    r92538ab r4559b34  
    2222#include <inttypes.h>
    2323
    24 #include "kernel/private.hfa"
     24#include "kernel_private.hfa"
    2525
    2626#include "bits/algorithm.hfa"
  • libcfa/src/concurrency/mutex.cfa

    r92538ab r4559b34  
    2121#include "mutex.hfa"
    2222
    23 #include "kernel/private.hfa"
     23#include "kernel_private.hfa"
    2424
    2525//-----------------------------------------------------------------------------
  • libcfa/src/concurrency/mutex_stmt.hfa

    r92538ab r4559b34  
    1212};
    1313
     14forall(L & | is_lock(L)) {
    1415
    15 struct __mutex_stmt_lock_guard {
    16     void ** lockarr;
    17     __lock_size_t count;
    18 };
     16    struct __mutex_stmt_lock_guard {
     17        L ** lockarr;
     18        __lock_size_t count;
     19    };
     20   
     21    static inline void ?{}( __mutex_stmt_lock_guard(L) & this, L * lockarr [], __lock_size_t count  ) {
     22        this.lockarr = lockarr;
     23        this.count = count;
    1924
    20 static inline void ?{}( __mutex_stmt_lock_guard & this, void * lockarr [], __lock_size_t count  ) {
    21     this.lockarr = lockarr;
    22     this.count = count;
     25        // Sort locks based on address
     26        __libcfa_small_sort(this.lockarr, count);
    2327
    24     // Sort locks based on address
    25     __libcfa_small_sort(this.lockarr, count);
    26 
    27     // acquire locks in order
    28     // for ( size_t i = 0; i < count; i++ ) {
    29     //     lock(*this.lockarr[i]);
    30     // }
    31 }
    32 
    33 static inline void ^?{}( __mutex_stmt_lock_guard & this ) with(this) {
    34     // for ( size_t i = count; i > 0; i-- ) {
    35     //     unlock(*lockarr[i - 1]);
    36     // }
    37 }
    38 
    39 forall(L & | is_lock(L)) {
     28        // acquire locks in order
     29        for ( size_t i = 0; i < count; i++ ) {
     30            lock(*this.lockarr[i]);
     31        }
     32    }
     33   
     34    static inline void ^?{}( __mutex_stmt_lock_guard(L) & this ) with(this) {
     35        for ( size_t i = count; i > 0; i-- ) {
     36            unlock(*lockarr[i - 1]);
     37        }
     38    }
    4039
    4140    struct scoped_lock {
     
    5251    }
    5352
    54     static inline void * __get_mutexstmt_lock_ptr( L & this ) {
     53    static inline L * __get_ptr( L & this ) {
    5554        return &this;
    5655    }
    5756
    58     static inline L __get_mutexstmt_lock_type( L & this );
     57    static inline L __get_type( L & this );
    5958
    60     static inline L __get_mutexstmt_lock_type( L * this );
     59    static inline L __get_type( L * this );
    6160}
  • libcfa/src/concurrency/preemption.cfa

    r92538ab r4559b34  
    1010// Created On       : Mon Jun 5 14:20:42 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Feb 17 11:18:57 2022
    13 // Update Count     : 59
     12// Last Modified On : Fri Nov  6 07:42:13 2020
     13// Update Count     : 54
    1414//
    1515
     
    3131#include "bits/debug.hfa"
    3232#include "bits/signal.hfa"
    33 #include "kernel/private.hfa"
     33#include "kernel_private.hfa"
    3434
    3535
     
    9797}
    9898
     99enum {
     100        PREEMPT_NORMAL    = 0,
     101        PREEMPT_TERMINATE = 1,
     102};
     103
    99104//=============================================================================================
    100105// Kernel Preemption logic
     
    238243//----------
    239244// special case for preemption since used often
    240 __attribute__((optimize("no-reorder-blocks"))) bool __preemption_enabled() {
     245bool __preemption_enabled() {
    241246        // create a assembler label before
    242247        // marked as clobber all to avoid movement
     
    659664        choose(sfp->si_value.sival_int) {
    660665                case PREEMPT_NORMAL   : ;// Normal case, nothing to do here
    661                 case PREEMPT_IO       : ;// I/O asked to stop spinning, nothing to do here
    662666                case PREEMPT_TERMINATE: verify( __atomic_load_n( &__cfaabi_tls.this_processor->do_terminate, __ATOMIC_SEQ_CST ) );
    663667                default:
  • libcfa/src/concurrency/ready_queue.cfa

    r92538ab r4559b34  
    2020
    2121
     22// #define USE_RELAXED_FIFO
     23// #define USE_WORK_STEALING
     24// #define USE_CPU_WORK_STEALING
    2225#define USE_AWARE_STEALING
    2326
    2427#include "bits/defs.hfa"
    2528#include "device/cpu.hfa"
    26 #include "kernel/cluster.hfa"
    27 #include "kernel/private.hfa"
    28 
    29 // #include <errno.h>
    30 // #include <unistd.h>
     29#include "kernel_private.hfa"
     30
     31#include "stdlib.hfa"
     32#include "limits.hfa"
     33#include "math.hfa"
     34
     35#include <errno.h>
     36#include <unistd.h>
     37
     38extern "C" {
     39        #include <sys/syscall.h>  // __NR_xxx
     40}
    3141
    3242#include "ready_subqueue.hfa"
     
    4050#endif
    4151
     52// No overriden function, no environment variable, no define
     53// fall back to a magic number
     54#ifndef __CFA_MAX_PROCESSORS__
     55        #define __CFA_MAX_PROCESSORS__ 1024
     56#endif
     57
     58#if   defined(USE_AWARE_STEALING)
     59        #define READYQ_SHARD_FACTOR 2
     60        #define SEQUENTIAL_SHARD 2
     61#elif defined(USE_CPU_WORK_STEALING)
     62        #define READYQ_SHARD_FACTOR 2
     63#elif defined(USE_RELAXED_FIFO)
     64        #define BIAS 4
     65        #define READYQ_SHARD_FACTOR 4
     66        #define SEQUENTIAL_SHARD 1
     67#elif defined(USE_WORK_STEALING)
     68        #define READYQ_SHARD_FACTOR 2
     69        #define SEQUENTIAL_SHARD 2
     70#else
     71        #error no scheduling strategy selected
     72#endif
     73
    4274static inline struct thread$ * try_pop(struct cluster * cltr, unsigned w __STATS(, __stats_readyQ_pop_t & stats));
    4375static inline struct thread$ * try_pop(struct cluster * cltr, unsigned i, unsigned j __STATS(, __stats_readyQ_pop_t & stats));
    4476static inline struct thread$ * search(struct cluster * cltr);
     77static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred);
     78
     79
     80// returns the maximum number of processors the RWLock support
     81__attribute__((weak)) unsigned __max_processors() {
     82        const char * max_cores_s = getenv("CFA_MAX_PROCESSORS");
     83        if(!max_cores_s) {
     84                __cfadbg_print_nolock(ready_queue, "No CFA_MAX_PROCESSORS in ENV\n");
     85                return __CFA_MAX_PROCESSORS__;
     86        }
     87
     88        char * endptr = 0p;
     89        long int max_cores_l = strtol(max_cores_s, &endptr, 10);
     90        if(max_cores_l < 1 || max_cores_l > 65535) {
     91                __cfadbg_print_nolock(ready_queue, "CFA_MAX_PROCESSORS out of range : %ld\n", max_cores_l);
     92                return __CFA_MAX_PROCESSORS__;
     93        }
     94        if('\0' != *endptr) {
     95                __cfadbg_print_nolock(ready_queue, "CFA_MAX_PROCESSORS not a decimal number : %s\n", max_cores_s);
     96                return __CFA_MAX_PROCESSORS__;
     97        }
     98
     99        return max_cores_l;
     100}
     101
     102#if   defined(CFA_HAVE_LINUX_LIBRSEQ)
     103        // No forward declaration needed
     104        #define __kernel_rseq_register rseq_register_current_thread
     105        #define __kernel_rseq_unregister rseq_unregister_current_thread
     106#elif defined(CFA_HAVE_LINUX_RSEQ_H)
     107        static void __kernel_raw_rseq_register  (void);
     108        static void __kernel_raw_rseq_unregister(void);
     109
     110        #define __kernel_rseq_register __kernel_raw_rseq_register
     111        #define __kernel_rseq_unregister __kernel_raw_rseq_unregister
     112#else
     113        // No forward declaration needed
     114        // No initialization needed
     115        static inline void noop(void) {}
     116
     117        #define __kernel_rseq_register noop
     118        #define __kernel_rseq_unregister noop
     119#endif
     120
     121//=======================================================================
     122// Cluster wide reader-writer lock
     123//=======================================================================
     124void  ?{}(__scheduler_RWLock_t & this) {
     125        this.max   = __max_processors();
     126        this.alloc = 0;
     127        this.ready = 0;
     128        this.data  = alloc(this.max);
     129        this.write_lock  = false;
     130
     131        /*paranoid*/ verify(__atomic_is_lock_free(sizeof(this.alloc), &this.alloc));
     132        /*paranoid*/ verify(__atomic_is_lock_free(sizeof(this.ready), &this.ready));
     133
     134}
     135void ^?{}(__scheduler_RWLock_t & this) {
     136        free(this.data);
     137}
     138
     139
     140//=======================================================================
     141// Lock-Free registering/unregistering of threads
     142unsigned register_proc_id( void ) with(*__scheduler_lock) {
     143        __kernel_rseq_register();
     144
     145        bool * handle = (bool *)&kernelTLS().sched_lock;
     146
     147        // Step - 1 : check if there is already space in the data
     148        uint_fast32_t s = ready;
     149
     150        // Check among all the ready
     151        for(uint_fast32_t i = 0; i < s; i++) {
     152                bool * volatile * cell = (bool * volatile *)&data[i]; // Cforall is bugged and the double volatiles causes problems
     153                /* paranoid */ verify( handle != *cell );
     154
     155                bool * null = 0p; // Re-write every loop since compare thrashes it
     156                if( __atomic_load_n(cell, (int)__ATOMIC_RELAXED) == null
     157                        && __atomic_compare_exchange_n( cell, &null, handle, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     158                        /* paranoid */ verify(i < ready);
     159                        /* paranoid */ verify( (kernelTLS().sched_id = i, true) );
     160                        return i;
     161                }
     162        }
     163
     164        if(max <= alloc) abort("Trying to create more than %ud processors", __scheduler_lock->max);
     165
     166        // Step - 2 : F&A to get a new spot in the array.
     167        uint_fast32_t n = __atomic_fetch_add(&alloc, 1, __ATOMIC_SEQ_CST);
     168        if(max <= n) abort("Trying to create more than %ud processors", __scheduler_lock->max);
     169
     170        // Step - 3 : Mark space as used and then publish it.
     171        data[n] = handle;
     172        while() {
     173                unsigned copy = n;
     174                if( __atomic_load_n(&ready, __ATOMIC_RELAXED) == n
     175                        && __atomic_compare_exchange_n(&ready, &copy, n + 1, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
     176                        break;
     177                Pause();
     178        }
     179
     180        // Return new spot.
     181        /* paranoid */ verify(n < ready);
     182        /* paranoid */ verify( (kernelTLS().sched_id = n, true) );
     183        return n;
     184}
     185
     186void unregister_proc_id( unsigned id ) with(*__scheduler_lock) {
     187        /* paranoid */ verify(id < ready);
     188        /* paranoid */ verify(id == kernelTLS().sched_id);
     189        /* paranoid */ verify(data[id] == &kernelTLS().sched_lock);
     190
     191        bool * volatile * cell = (bool * volatile *)&data[id]; // Cforall is bugged and the double volatiles causes problems
     192
     193        __atomic_store_n(cell, 0p, __ATOMIC_RELEASE);
     194
     195        __kernel_rseq_unregister();
     196}
     197
     198//-----------------------------------------------------------------------
     199// Writer side : acquire when changing the ready queue, e.g. adding more
     200//  queues or removing them.
     201uint_fast32_t ready_mutate_lock( void ) with(*__scheduler_lock) {
     202        /* paranoid */ verify( ! __preemption_enabled() );
     203
     204        // Step 1 : lock global lock
     205        // It is needed to avoid processors that register mid Critical-Section
     206        //   to simply lock their own lock and enter.
     207        __atomic_acquire( &write_lock );
     208
     209        // Make sure we won't deadlock ourself
     210        // Checking before acquiring the writer lock isn't safe
     211        // because someone else could have locked us.
     212        /* paranoid */ verify( ! kernelTLS().sched_lock );
     213
     214        // Step 2 : lock per-proc lock
     215        // Processors that are currently being registered aren't counted
     216        //   but can't be in read_lock or in the critical section.
     217        // All other processors are counted
     218        uint_fast32_t s = ready;
     219        for(uint_fast32_t i = 0; i < s; i++) {
     220                volatile bool * llock = data[i];
     221                if(llock) __atomic_acquire( llock );
     222        }
     223
     224        /* paranoid */ verify( ! __preemption_enabled() );
     225        return s;
     226}
     227
     228void ready_mutate_unlock( uint_fast32_t last_s ) with(*__scheduler_lock) {
     229        /* paranoid */ verify( ! __preemption_enabled() );
     230
     231        // Step 1 : release local locks
     232        // This must be done while the global lock is held to avoid
     233        //   threads that where created mid critical section
     234        //   to race to lock their local locks and have the writer
     235        //   immidiately unlock them
     236        // Alternative solution : return s in write_lock and pass it to write_unlock
     237        for(uint_fast32_t i = 0; i < last_s; i++) {
     238                volatile bool * llock = data[i];
     239                if(llock) __atomic_store_n(llock, (bool)false, __ATOMIC_RELEASE);
     240        }
     241
     242        // Step 2 : release global lock
     243        /*paranoid*/ assert(true == write_lock);
     244        __atomic_store_n(&write_lock, (bool)false, __ATOMIC_RELEASE);
     245
     246        /* paranoid */ verify( ! __preemption_enabled() );
     247}
     248
     249//=======================================================================
     250// caches handling
     251
     252struct __attribute__((aligned(128))) __ready_queue_caches_t {
     253        // Count States:
     254        // - 0  : No one is looking after this cache
     255        // - 1  : No one is looking after this cache, BUT it's not empty
     256        // - 2+ : At least one processor is looking after this cache
     257        volatile unsigned count;
     258};
     259
     260void  ?{}(__ready_queue_caches_t & this) { this.count = 0; }
     261void ^?{}(__ready_queue_caches_t & this) {}
     262
     263static inline void depart(__ready_queue_caches_t & cache) {
     264        /* paranoid */ verify( cache.count > 1);
     265        __atomic_fetch_add(&cache.count, -1, __ATOMIC_SEQ_CST);
     266        /* paranoid */ verify( cache.count != 0);
     267        /* paranoid */ verify( cache.count < 65536 ); // This verify assumes no cluster will have more than 65000 kernel threads mapped to a single cache, which could be correct but is super weird.
     268}
     269
     270static inline void arrive(__ready_queue_caches_t & cache) {
     271        // for() {
     272        //      unsigned expected = cache.count;
     273        //      unsigned desired  = 0 == expected ? 2 : expected + 1;
     274        // }
     275}
    45276
    46277//=======================================================================
    47278// Cforall Ready Queue used for scheduling
    48279//=======================================================================
    49 // void ?{}(__ready_queue_t & this) with (this) {
    50 //      lanes.data   = 0p;
    51 //      lanes.tscs   = 0p;
    52 //      lanes.caches = 0p;
    53 //      lanes.count  = 0;
    54 // }
    55 
    56 // void ^?{}(__ready_queue_t & this) with (this) {
    57 //      free(lanes.data);
    58 //      free(lanes.tscs);
    59 //      free(lanes.caches);
    60 // }
     280unsigned long long moving_average(unsigned long long currtsc, unsigned long long instsc, unsigned long long old_avg) {
     281        /* paranoid */ verifyf( currtsc < 45000000000000000, "Suspiciously large current time: %'llu (%llx)\n", currtsc, currtsc );
     282        /* paranoid */ verifyf( instsc  < 45000000000000000, "Suspiciously large insert time: %'llu (%llx)\n", instsc, instsc );
     283        /* paranoid */ verifyf( old_avg < 15000000000000, "Suspiciously large previous average: %'llu (%llx)\n", old_avg, old_avg );
     284
     285        const unsigned long long new_val = currtsc > instsc ? currtsc - instsc : 0;
     286        const unsigned long long total_weight = 16;
     287        const unsigned long long new_weight   = 4;
     288        const unsigned long long old_weight = total_weight - new_weight;
     289        const unsigned long long ret = ((new_weight * new_val) + (old_weight * old_avg)) / total_weight;
     290        return ret;
     291}
     292
     293void ?{}(__ready_queue_t & this) with (this) {
     294        #if defined(USE_CPU_WORK_STEALING)
     295                lanes.count = cpu_info.hthrd_count * READYQ_SHARD_FACTOR;
     296                lanes.data = alloc( lanes.count );
     297                lanes.tscs = alloc( lanes.count );
     298                lanes.help = alloc( cpu_info.hthrd_count );
     299
     300                for( idx; (size_t)lanes.count ) {
     301                        (lanes.data[idx]){};
     302                        lanes.tscs[idx].tv = rdtscl();
     303                        lanes.tscs[idx].ma = rdtscl();
     304                }
     305                for( idx; (size_t)cpu_info.hthrd_count ) {
     306                        lanes.help[idx].src = 0;
     307                        lanes.help[idx].dst = 0;
     308                        lanes.help[idx].tri = 0;
     309                }
     310        #else
     311                lanes.data   = 0p;
     312                lanes.tscs   = 0p;
     313                lanes.caches = 0p;
     314                lanes.help   = 0p;
     315                lanes.count  = 0;
     316        #endif
     317}
     318
     319void ^?{}(__ready_queue_t & this) with (this) {
     320        #if !defined(USE_CPU_WORK_STEALING)
     321                verify( SEQUENTIAL_SHARD == lanes.count );
     322        #endif
     323
     324        free(lanes.data);
     325        free(lanes.tscs);
     326        free(lanes.caches);
     327        free(lanes.help);
     328}
    61329
    62330//-----------------------------------------------------------------------
    63 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->sched) {
    64         processor * const proc = kernelTLS().this_processor;
    65         const bool external = (!proc) || (cltr != proc->cltr);
    66         const bool remote   = hint == UNPARK_REMOTE;
    67         const size_t lanes_count = readyQ.count;
    68 
    69         /* paranoid */ verify( __shard_factor.readyq > 0 );
    70         /* paranoid */ verify( lanes_count > 0 );
    71 
    72         unsigned i;
    73         if( external || remote ) {
    74                 // Figure out where thread was last time and make sure it's valid
    75                 /* paranoid */ verify(thrd->preferred >= 0);
    76                 unsigned start = thrd->preferred * __shard_factor.readyq;
    77                 if(start < lanes_count) {
    78                         do {
    79                                 unsigned r = __tls_rand();
    80                                 i = start + (r % __shard_factor.readyq);
    81                                 /* paranoid */ verify( i < lanes_count );
    82                                 // If we can't lock it retry
    83                         } while( !__atomic_try_acquire( &readyQ.data[i].lock ) );
     331#if defined(USE_AWARE_STEALING)
     332        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
     333                processor * const proc = kernelTLS().this_processor;
     334                const bool external = (!proc) || (cltr != proc->cltr);
     335                const bool remote   = hint == UNPARK_REMOTE;
     336
     337                unsigned i;
     338                if( external || remote ) {
     339                        // Figure out where thread was last time and make sure it's valid
     340                        /* paranoid */ verify(thrd->preferred >= 0);
     341                        if(thrd->preferred * READYQ_SHARD_FACTOR < lanes.count) {
     342                                /* paranoid */ verify(thrd->preferred * READYQ_SHARD_FACTOR < lanes.count);
     343                                unsigned start = thrd->preferred * READYQ_SHARD_FACTOR;
     344                                do {
     345                                        unsigned r = __tls_rand();
     346                                        i = start + (r % READYQ_SHARD_FACTOR);
     347                                        /* paranoid */ verify( i < lanes.count );
     348                                        // If we can't lock it retry
     349                                } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
     350                        } else {
     351                                do {
     352                                        i = __tls_rand() % lanes.count;
     353                                } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
     354                        }
    84355                } else {
    85356                        do {
    86                                 i = __tls_rand() % lanes_count;
    87                         } while( !__atomic_try_acquire( &readyQ.data[i].lock ) );
    88                 }
    89         } else {
     357                                unsigned r = proc->rdq.its++;
     358                                i = proc->rdq.id + (r % READYQ_SHARD_FACTOR);
     359                                /* paranoid */ verify( i < lanes.count );
     360                                // If we can't lock it retry
     361                        } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
     362                }
     363
     364                // Actually push it
     365                push(lanes.data[i], thrd);
     366
     367                // Unlock and return
     368                __atomic_unlock( &lanes.data[i].lock );
     369
     370                #if !defined(__CFA_NO_STATISTICS__)
     371                        if(unlikely(external || remote)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED);
     372                        else __tls_stats()->ready.push.local.success++;
     373                #endif
     374        }
     375
     376        static inline unsigned long long calc_cutoff(const unsigned long long ctsc, const processor * proc, __ready_queue_t & rdq) {
     377                unsigned start = proc->rdq.id;
     378                unsigned long long max = 0;
     379                for(i; READYQ_SHARD_FACTOR) {
     380                        unsigned long long ptsc = ts(rdq.lanes.data[start + i]);
     381                        if(ptsc != -1ull) {
     382                                /* paranoid */ verify( start + i < rdq.lanes.count );
     383                                unsigned long long tsc = moving_average(ctsc, ptsc, rdq.lanes.tscs[start + i].ma);
     384                                if(tsc > max) max = tsc;
     385                        }
     386                }
     387                return (max + 2 * max) / 2;
     388        }
     389
     390        __attribute__((hot)) struct thread$ * pop_fast(struct cluster * cltr) with (cltr->ready_queue) {
     391                /* paranoid */ verify( lanes.count > 0 );
     392                /* paranoid */ verify( kernelTLS().this_processor );
     393                /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes.count );
     394
     395                processor * const proc = kernelTLS().this_processor;
     396                unsigned this = proc->rdq.id;
     397                /* paranoid */ verify( this < lanes.count );
     398                __cfadbg_print_safe(ready_queue, "Kernel : pop from %u\n", this);
     399
     400                // Figure out the current cpu and make sure it is valid
     401                const int cpu = __kernel_getcpu();
     402                /* paranoid */ verify(cpu >= 0);
     403                /* paranoid */ verify(cpu < cpu_info.hthrd_count);
     404                unsigned this_cache = cpu_info.llc_map[cpu].cache;
     405
     406                // Super important: don't write the same value over and over again
     407                // We want to maximise our chances that his particular values stays in cache
     408                if(lanes.caches[this / READYQ_SHARD_FACTOR].id != this_cache)
     409                        __atomic_store_n(&lanes.caches[this / READYQ_SHARD_FACTOR].id, this_cache, __ATOMIC_RELAXED);
     410
     411                const unsigned long long ctsc = rdtscl();
     412
     413                if(proc->rdq.target == MAX) {
     414                        uint64_t chaos = __tls_rand();
     415                        unsigned ext = chaos & 0xff;
     416                        unsigned other  = (chaos >> 8) % (lanes.count);
     417
     418                        if(ext < 3 || __atomic_load_n(&lanes.caches[other / READYQ_SHARD_FACTOR].id, __ATOMIC_RELAXED) == this_cache) {
     419                                proc->rdq.target = other;
     420                        }
     421                }
     422                else {
     423                        const unsigned target = proc->rdq.target;
     424                        __cfadbg_print_safe(ready_queue, "Kernel : %u considering helping %u, tcsc %llu\n", this, target, lanes.tscs[target].tv);
     425                        /* paranoid */ verify( lanes.tscs[target].tv != MAX );
     426                        if(target < lanes.count) {
     427                                const unsigned long long cutoff = calc_cutoff(ctsc, proc, cltr->ready_queue);
     428                                const unsigned long long age = moving_average(ctsc, lanes.tscs[target].tv, lanes.tscs[target].ma);
     429                                __cfadbg_print_safe(ready_queue, "Kernel : Help attempt on %u from %u, age %'llu vs cutoff %'llu, %s\n", target, this, age, cutoff, age > cutoff ? "yes" : "no");
     430                                if(age > cutoff) {
     431                                        thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help));
     432                                        if(t) return t;
     433                                }
     434                        }
     435                        proc->rdq.target = MAX;
     436                }
     437
     438                for(READYQ_SHARD_FACTOR) {
     439                        unsigned i = this + (proc->rdq.itr++ % READYQ_SHARD_FACTOR);
     440                        if(thread$ * t = try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.local))) return t;
     441                }
     442
     443                // All lanes where empty return 0p
     444                return 0p;
     445
     446        }
     447        __attribute__((hot)) struct thread$ * pop_slow(struct cluster * cltr) with (cltr->ready_queue) {
     448                unsigned i = __tls_rand() % lanes.count;
     449                return try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.steal));
     450        }
     451        __attribute__((hot)) struct thread$ * pop_search(struct cluster * cltr) {
     452                return search(cltr);
     453        }
     454#endif
     455#if defined(USE_CPU_WORK_STEALING)
     456        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
     457                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
     458
     459                processor * const proc = kernelTLS().this_processor;
     460                const bool external = (!proc) || (cltr != proc->cltr);
     461
     462                // Figure out the current cpu and make sure it is valid
     463                const int cpu = __kernel_getcpu();
     464                /* paranoid */ verify(cpu >= 0);
     465                /* paranoid */ verify(cpu < cpu_info.hthrd_count);
     466                /* paranoid */ verify(cpu * READYQ_SHARD_FACTOR < lanes.count);
     467
     468                // Figure out where thread was last time and make sure it's
     469                /* paranoid */ verify(thrd->preferred >= 0);
     470                /* paranoid */ verify(thrd->preferred < cpu_info.hthrd_count);
     471                /* paranoid */ verify(thrd->preferred * READYQ_SHARD_FACTOR < lanes.count);
     472                const int prf = thrd->preferred * READYQ_SHARD_FACTOR;
     473
     474                const cpu_map_entry_t & map;
     475                choose(hint) {
     476                        case UNPARK_LOCAL : &map = &cpu_info.llc_map[cpu];
     477                        case UNPARK_REMOTE: &map = &cpu_info.llc_map[prf];
     478                }
     479                /* paranoid */ verify(map.start * READYQ_SHARD_FACTOR < lanes.count);
     480                /* paranoid */ verify(map.self * READYQ_SHARD_FACTOR < lanes.count);
     481                /* paranoid */ verifyf((map.start + map.count) * READYQ_SHARD_FACTOR <= lanes.count, "have %zu lanes but map can go up to %u", lanes.count, (map.start + map.count) * READYQ_SHARD_FACTOR);
     482
     483                const int start = map.self * READYQ_SHARD_FACTOR;
     484                unsigned i;
    90485                do {
    91                         unsigned r = proc->rdq.its++;
    92                         i = proc->rdq.id + (r % __shard_factor.readyq);
    93                         /* paranoid */ verify( i < lanes_count );
     486                        unsigned r;
     487                        if(unlikely(external)) { r = __tls_rand(); }
     488                        else { r = proc->rdq.its++; }
     489                        choose(hint) {
     490                                case UNPARK_LOCAL : i = start + (r % READYQ_SHARD_FACTOR);
     491                                case UNPARK_REMOTE: i = prf   + (r % READYQ_SHARD_FACTOR);
     492                        }
    94493                        // If we can't lock it retry
    95                 } while( !__atomic_try_acquire( &readyQ.data[i].lock ) );
    96         }
    97 
    98         // Actually push it
    99         push(readyQ.data[i], thrd);
    100 
    101         // Unlock and return
    102         __atomic_unlock( &readyQ.data[i].lock );
    103 
    104         #if !defined(__CFA_NO_STATISTICS__)
    105                 if(unlikely(external || remote)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED);
    106                 else __tls_stats()->ready.push.local.success++;
    107         #endif
    108 }
    109 
    110 __attribute__((hot)) struct thread$ * pop_fast(struct cluster * cltr) with (cltr->sched) {
    111         const size_t lanes_count = readyQ.count;
    112 
    113         /* paranoid */ verify( __shard_factor.readyq > 0 );
    114         /* paranoid */ verify( lanes_count > 0 );
    115         /* paranoid */ verify( kernelTLS().this_processor );
    116         /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes_count );
    117 
    118         processor * const proc = kernelTLS().this_processor;
    119         unsigned this = proc->rdq.id;
    120         /* paranoid */ verify( this < lanes_count );
    121         __cfadbg_print_safe(ready_queue, "Kernel : pop from %u\n", this);
    122 
    123         // Figure out the current cache is
    124         const unsigned this_cache = cache_id(cltr, this / __shard_factor.readyq);
    125         const unsigned long long ctsc = rdtscl();
    126 
    127         if(proc->rdq.target == MAX) {
    128                 uint64_t chaos = __tls_rand();
    129                 unsigned ext = chaos & 0xff;
    130                 unsigned other  = (chaos >> 8) % (lanes_count);
    131 
    132                 if(ext < 3 || __atomic_load_n(&caches[other / __shard_factor.readyq].id, __ATOMIC_RELAXED) == this_cache) {
    133                         proc->rdq.target = other;
    134                 }
    135         }
    136         else {
    137                 const unsigned target = proc->rdq.target;
    138                 __cfadbg_print_safe(ready_queue, "Kernel : %u considering helping %u, tcsc %llu\n", this, target, readyQ.tscs[target].tv);
    139                 /* paranoid */ verify( readyQ.tscs[target].tv != MAX );
    140                 if(target < lanes_count) {
    141                         const unsigned long long cutoff = calc_cutoff(ctsc, proc->rdq.id, lanes_count, cltr->sched.readyQ.data, cltr->sched.readyQ.tscs, __shard_factor.readyq);
    142                         const unsigned long long age = moving_average(ctsc, readyQ.tscs[target].tv, readyQ.tscs[target].ma);
    143                         __cfadbg_print_safe(ready_queue, "Kernel : Help attempt on %u from %u, age %'llu vs cutoff %'llu, %s\n", target, this, age, cutoff, age > cutoff ? "yes" : "no");
    144                         if(age > cutoff) {
     494                } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
     495
     496                // Actually push it
     497                push(lanes.data[i], thrd);
     498
     499                // Unlock and return
     500                __atomic_unlock( &lanes.data[i].lock );
     501
     502                #if !defined(__CFA_NO_STATISTICS__)
     503                        if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED);
     504                        else __tls_stats()->ready.push.local.success++;
     505                #endif
     506
     507                __cfadbg_print_safe(ready_queue, "Kernel : Pushed %p on cluster %p (idx: %u, mask %llu, first %d)\n", thrd, cltr, i, used.mask[0], lane_first);
     508
     509        }
     510
     511        // Pop from the ready queue from a given cluster
     512        __attribute__((hot)) thread$ * pop_fast(struct cluster * cltr) with (cltr->ready_queue) {
     513                /* paranoid */ verify( lanes.count > 0 );
     514                /* paranoid */ verify( kernelTLS().this_processor );
     515
     516                processor * const proc = kernelTLS().this_processor;
     517                const int cpu = __kernel_getcpu();
     518                /* paranoid */ verify(cpu >= 0);
     519                /* paranoid */ verify(cpu < cpu_info.hthrd_count);
     520                /* paranoid */ verify(cpu * READYQ_SHARD_FACTOR < lanes.count);
     521
     522                const cpu_map_entry_t & map = cpu_info.llc_map[cpu];
     523                /* paranoid */ verify(map.start * READYQ_SHARD_FACTOR < lanes.count);
     524                /* paranoid */ verify(map.self * READYQ_SHARD_FACTOR < lanes.count);
     525                /* paranoid */ verifyf((map.start + map.count) * READYQ_SHARD_FACTOR <= lanes.count, "have %zu lanes but map can go up to %u", lanes.count, (map.start + map.count) * READYQ_SHARD_FACTOR);
     526
     527                const int start = map.self * READYQ_SHARD_FACTOR;
     528                const unsigned long long ctsc = rdtscl();
     529
     530                // Did we already have a help target
     531                if(proc->rdq.target == MAX) {
     532                        unsigned long long max = 0;
     533                        for(i; READYQ_SHARD_FACTOR) {
     534                                unsigned long long tsc = moving_average(ctsc, ts(lanes.data[start + i]), lanes.tscs[start + i].ma);
     535                                if(tsc > max) max = tsc;
     536                        }
     537                        //  proc->rdq.cutoff = (max + 2 * max) / 2;
     538                        /* paranoid */ verify(lanes.count < 65536); // The following code assumes max 65536 cores.
     539                        /* paranoid */ verify(map.count < 65536); // The following code assumes max 65536 cores.
     540
     541                        if(0 == (__tls_rand() % 100)) {
     542                                proc->rdq.target = __tls_rand() % lanes.count;
     543                        } else {
     544                                unsigned cpu_chaos = map.start + (__tls_rand() % map.count);
     545                                proc->rdq.target = (cpu_chaos * READYQ_SHARD_FACTOR) + (__tls_rand() % READYQ_SHARD_FACTOR);
     546                                /* paranoid */ verify(proc->rdq.target >= (map.start * READYQ_SHARD_FACTOR));
     547                                /* paranoid */ verify(proc->rdq.target <  ((map.start + map.count) * READYQ_SHARD_FACTOR));
     548                        }
     549
     550                        /* paranoid */ verify(proc->rdq.target != MAX);
     551                }
     552                else {
     553                        unsigned long long max = 0;
     554                        for(i; READYQ_SHARD_FACTOR) {
     555                                unsigned long long tsc = moving_average(ctsc, ts(lanes.data[start + i]), lanes.tscs[start + i].ma);
     556                                if(tsc > max) max = tsc;
     557                        }
     558                        const unsigned long long cutoff = (max + 2 * max) / 2;
     559                        {
     560                                unsigned target = proc->rdq.target;
     561                                proc->rdq.target = MAX;
     562                                lanes.help[target / READYQ_SHARD_FACTOR].tri++;
     563                                if(moving_average(ctsc, lanes.tscs[target].tv, lanes.tscs[target].ma) > cutoff) {
     564                                        thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help));
     565                                        proc->rdq.last = target;
     566                                        if(t) return t;
     567                                }
     568                                proc->rdq.target = MAX;
     569                        }
     570
     571                        unsigned last = proc->rdq.last;
     572                        if(last != MAX && moving_average(ctsc, lanes.tscs[last].tv, lanes.tscs[last].ma) > cutoff) {
     573                                thread$ * t = try_pop(cltr, last __STATS(, __tls_stats()->ready.pop.help));
     574                                if(t) return t;
     575                        }
     576                        else {
     577                                proc->rdq.last = MAX;
     578                        }
     579                }
     580
     581                for(READYQ_SHARD_FACTOR) {
     582                        unsigned i = start + (proc->rdq.itr++ % READYQ_SHARD_FACTOR);
     583                        if(thread$ * t = try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.local))) return t;
     584                }
     585
     586                // All lanes where empty return 0p
     587                return 0p;
     588        }
     589
     590        __attribute__((hot)) struct thread$ * pop_slow(struct cluster * cltr) with (cltr->ready_queue) {
     591                processor * const proc = kernelTLS().this_processor;
     592                unsigned last = proc->rdq.last;
     593                if(last != MAX) {
     594                        struct thread$ * t = try_pop(cltr, last __STATS(, __tls_stats()->ready.pop.steal));
     595                        if(t) return t;
     596                        proc->rdq.last = MAX;
     597                }
     598
     599                unsigned i = __tls_rand() % lanes.count;
     600                return try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.steal));
     601        }
     602        __attribute__((hot)) struct thread$ * pop_search(struct cluster * cltr) {
     603                return search(cltr);
     604        }
     605#endif
     606#if defined(USE_RELAXED_FIFO)
     607        //-----------------------------------------------------------------------
     608        // get index from random number with or without bias towards queues
     609        static inline [unsigned, bool] idx_from_r(unsigned r, unsigned preferred) {
     610                unsigned i;
     611                bool local;
     612                unsigned rlow  = r % BIAS;
     613                unsigned rhigh = r / BIAS;
     614                if((0 != rlow) && preferred >= 0) {
     615                        // (BIAS - 1) out of BIAS chances
     616                        // Use perferred queues
     617                        i = preferred + (rhigh % READYQ_SHARD_FACTOR);
     618                        local = true;
     619                }
     620                else {
     621                        // 1 out of BIAS chances
     622                        // Use all queues
     623                        i = rhigh;
     624                        local = false;
     625                }
     626                return [i, local];
     627        }
     628
     629        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
     630                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
     631
     632                const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
     633                /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count );
     634
     635                bool local;
     636                int preferred = external ? -1 : kernelTLS().this_processor->rdq.id;
     637
     638                // Try to pick a lane and lock it
     639                unsigned i;
     640                do {
     641                        // Pick the index of a lane
     642                        unsigned r = __tls_rand_fwd();
     643                        [i, local] = idx_from_r(r, preferred);
     644
     645                        i %= __atomic_load_n( &lanes.count, __ATOMIC_RELAXED );
     646
     647                        #if !defined(__CFA_NO_STATISTICS__)
     648                                if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.attempt, 1, __ATOMIC_RELAXED);
     649                                else if(local) __tls_stats()->ready.push.local.attempt++;
     650                                else __tls_stats()->ready.push.share.attempt++;
     651                        #endif
     652
     653                        // If we can't lock it retry
     654                } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
     655
     656                // Actually push it
     657                push(lanes.data[i], thrd);
     658
     659                // Unlock and return
     660                __atomic_unlock( &lanes.data[i].lock );
     661
     662                // Mark the current index in the tls rng instance as having an item
     663                __tls_rand_advance_bck();
     664
     665                __cfadbg_print_safe(ready_queue, "Kernel : Pushed %p on cluster %p (idx: %u, mask %llu, first %d)\n", thrd, cltr, i, used.mask[0], lane_first);
     666
     667                // Update statistics
     668                #if !defined(__CFA_NO_STATISTICS__)
     669                        if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED);
     670                        else if(local) __tls_stats()->ready.push.local.success++;
     671                        else __tls_stats()->ready.push.share.success++;
     672                #endif
     673        }
     674
     675        // Pop from the ready queue from a given cluster
     676        __attribute__((hot)) thread$ * pop_fast(struct cluster * cltr) with (cltr->ready_queue) {
     677                /* paranoid */ verify( lanes.count > 0 );
     678                /* paranoid */ verify( kernelTLS().this_processor );
     679                /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes.count );
     680
     681                unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED );
     682                int preferred = kernelTLS().this_processor->rdq.id;
     683
     684
     685                // As long as the list is not empty, try finding a lane that isn't empty and pop from it
     686                for(25) {
     687                        // Pick two lists at random
     688                        unsigned ri = __tls_rand_bck();
     689                        unsigned rj = __tls_rand_bck();
     690
     691                        unsigned i, j;
     692                        __attribute__((unused)) bool locali, localj;
     693                        [i, locali] = idx_from_r(ri, preferred);
     694                        [j, localj] = idx_from_r(rj, preferred);
     695
     696                        i %= count;
     697                        j %= count;
     698
     699                        // try popping from the 2 picked lists
     700                        struct thread$ * thrd = try_pop(cltr, i, j __STATS(, *(locali || localj ? &__tls_stats()->ready.pop.local : &__tls_stats()->ready.pop.help)));
     701                        if(thrd) {
     702                                return thrd;
     703                        }
     704                }
     705
     706                // All lanes where empty return 0p
     707                return 0p;
     708        }
     709
     710        __attribute__((hot)) struct thread$ * pop_slow(struct cluster * cltr) { return pop_fast(cltr); }
     711        __attribute__((hot)) struct thread$ * pop_search(struct cluster * cltr) {
     712                return search(cltr);
     713        }
     714#endif
     715#if defined(USE_WORK_STEALING)
     716        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
     717                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
     718
     719                // #define USE_PREFERRED
     720                #if !defined(USE_PREFERRED)
     721                const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
     722                /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count );
     723                #else
     724                        unsigned preferred = thrd->preferred;
     725                        const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || preferred == MAX || thrd->curr_cluster != cltr;
     726                        /* paranoid */ verifyf(external || preferred < lanes.count, "Invalid preferred queue %u for %u lanes", preferred, lanes.count );
     727
     728                        unsigned r = preferred % READYQ_SHARD_FACTOR;
     729                        const unsigned start = preferred - r;
     730                #endif
     731
     732                // Try to pick a lane and lock it
     733                unsigned i;
     734                do {
     735                        #if !defined(__CFA_NO_STATISTICS__)
     736                                if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.attempt, 1, __ATOMIC_RELAXED);
     737                                else __tls_stats()->ready.push.local.attempt++;
     738                        #endif
     739
     740                        if(unlikely(external)) {
     741                                i = __tls_rand() % lanes.count;
     742                        }
     743                        else {
     744                                #if !defined(USE_PREFERRED)
     745                                        processor * proc = kernelTLS().this_processor;
     746                                        unsigned r = proc->rdq.its++;
     747                                        i =  proc->rdq.id + (r % READYQ_SHARD_FACTOR);
     748                                #else
     749                                        i = start + (r++ % READYQ_SHARD_FACTOR);
     750                                #endif
     751                        }
     752                        // If we can't lock it retry
     753                } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
     754
     755                // Actually push it
     756                push(lanes.data[i], thrd);
     757
     758                // Unlock and return
     759                __atomic_unlock( &lanes.data[i].lock );
     760
     761                #if !defined(__CFA_NO_STATISTICS__)
     762                        if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED);
     763                        else __tls_stats()->ready.push.local.success++;
     764                #endif
     765
     766                __cfadbg_print_safe(ready_queue, "Kernel : Pushed %p on cluster %p (idx: %u, mask %llu, first %d)\n", thrd, cltr, i, used.mask[0], lane_first);
     767        }
     768
     769        // Pop from the ready queue from a given cluster
     770        __attribute__((hot)) thread$ * pop_fast(struct cluster * cltr) with (cltr->ready_queue) {
     771                /* paranoid */ verify( lanes.count > 0 );
     772                /* paranoid */ verify( kernelTLS().this_processor );
     773                /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes.count );
     774
     775                processor * proc = kernelTLS().this_processor;
     776
     777                if(proc->rdq.target == MAX) {
     778                        unsigned long long min = ts(lanes.data[proc->rdq.id]);
     779                        for(int i = 0; i < READYQ_SHARD_FACTOR; i++) {
     780                                unsigned long long tsc = ts(lanes.data[proc->rdq.id + i]);
     781                                if(tsc < min) min = tsc;
     782                        }
     783                        proc->rdq.cutoff = min;
     784                        proc->rdq.target = __tls_rand() % lanes.count;
     785                }
     786                else {
     787                        unsigned target = proc->rdq.target;
     788                        proc->rdq.target = MAX;
     789                        const unsigned long long bias = 0; //2_500_000_000;
     790                        const unsigned long long cutoff = proc->rdq.cutoff > bias ? proc->rdq.cutoff - bias : proc->rdq.cutoff;
     791                        if(lanes.tscs[target].tv < cutoff && ts(lanes.data[target]) < cutoff) {
    145792                                thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help));
    146793                                if(t) return t;
    147794                        }
    148795                }
    149                 proc->rdq.target = MAX;
    150         }
    151 
    152         for(__shard_factor.readyq) {
    153                 unsigned i = this + (proc->rdq.itr++ % __shard_factor.readyq);
    154                 if(thread$ * t = try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.local))) return t;
    155         }
    156 
    157         // All lanes where empty return 0p
    158         return 0p;
    159 
    160 }
    161 __attribute__((hot)) struct thread$ * pop_slow(struct cluster * cltr) {
    162         unsigned i = __tls_rand() % (cltr->sched.readyQ.count);
    163         return try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.steal));
    164 }
    165 __attribute__((hot)) struct thread$ * pop_search(struct cluster * cltr) {
    166         return search(cltr);
    167 }
     796
     797                for(READYQ_SHARD_FACTOR) {
     798                        unsigned i = proc->rdq.id + (proc->rdq.itr++ % READYQ_SHARD_FACTOR);
     799                        if(thread$ * t = try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.local))) return t;
     800                }
     801                return 0p;
     802        }
     803
     804        __attribute__((hot)) struct thread$ * pop_slow(struct cluster * cltr) with (cltr->ready_queue) {
     805                unsigned i = __tls_rand() % lanes.count;
     806                return try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.steal));
     807        }
     808
     809        __attribute__((hot)) struct thread$ * pop_search(struct cluster * cltr) with (cltr->ready_queue) {
     810                return search(cltr);
     811        }
     812#endif
    168813
    169814//=======================================================================
     
    175820//-----------------------------------------------------------------------
    176821// try to pop from a lane given by index w
    177 static inline struct thread$ * try_pop(struct cluster * cltr, unsigned w __STATS(, __stats_readyQ_pop_t & stats)) with (cltr->sched) {
    178         /* paranoid */ verify( w < readyQ.count );
     822static inline struct thread$ * try_pop(struct cluster * cltr, unsigned w __STATS(, __stats_readyQ_pop_t & stats)) with (cltr->ready_queue) {
     823        /* paranoid */ verify( w < lanes.count );
    179824        __STATS( stats.attempt++; )
    180825
    181826        // Get relevant elements locally
    182         __intrusive_lane_t & lane = readyQ.data[w];
     827        __intrusive_lane_t & lane = lanes.data[w];
    183828
    184829        // If list looks empty retry
     
    200845        // Actually pop the list
    201846        struct thread$ * thrd;
    202         unsigned long long ts_prev = ts(lane);
    203         unsigned long long ts_next;
    204         [thrd, ts_next] = pop(lane);
     847        #if defined(USE_AWARE_STEALING) || defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING)
     848                unsigned long long tsc_before = ts(lane);
     849        #endif
     850        unsigned long long tsv;
     851        [thrd, tsv] = pop(lane);
    205852
    206853        /* paranoid */ verify(thrd);
    207         /* paranoid */ verify(ts_next);
     854        /* paranoid */ verify(tsv);
    208855        /* paranoid */ verify(lane.lock);
    209856
     
    214861        __STATS( stats.success++; )
    215862
    216         touch_tsc(readyQ.tscs, w, ts_prev, ts_next);
    217 
    218         thrd->preferred = w / __shard_factor.readyq;
     863        #if defined(USE_AWARE_STEALING) || defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING)
     864                if (tsv != MAX) {
     865                        unsigned long long now = rdtscl();
     866                        unsigned long long pma = __atomic_load_n(&lanes.tscs[w].ma, __ATOMIC_RELAXED);
     867                        __atomic_store_n(&lanes.tscs[w].tv, tsv, __ATOMIC_RELAXED);
     868                        __atomic_store_n(&lanes.tscs[w].ma, moving_average(now, tsc_before, pma), __ATOMIC_RELAXED);
     869                }
     870        #endif
     871
     872        #if defined(USE_AWARE_STEALING) || defined(USE_CPU_WORK_STEALING)
     873                thrd->preferred = w / READYQ_SHARD_FACTOR;
     874        #else
     875                thrd->preferred = w;
     876        #endif
    219877
    220878        // return the popped thread
     
    225883// try to pop from any lanes making sure you don't miss any threads push
    226884// before the start of the function
    227 static inline struct thread$ * search(struct cluster * cltr) {
    228         const size_t lanes_count = cltr->sched.readyQ.count;
    229         /* paranoid */ verify( lanes_count > 0 );
    230         unsigned count = __atomic_load_n( &lanes_count, __ATOMIC_RELAXED );
     885static inline struct thread$ * search(struct cluster * cltr) with (cltr->ready_queue) {
     886        /* paranoid */ verify( lanes.count > 0 );
     887        unsigned count = __atomic_load_n( &lanes.count, __ATOMIC_RELAXED );
    231888        unsigned offset = __tls_rand();
    232889        for(i; count) {
     
    245902// get preferred ready for new thread
    246903unsigned ready_queue_new_preferred() {
    247         unsigned pref = MAX;
     904        unsigned pref = 0;
    248905        if(struct thread$ * thrd = publicTLS_get( this_thread )) {
    249906                pref = thrd->preferred;
    250907        }
     908        else {
     909                #if defined(USE_CPU_WORK_STEALING)
     910                        pref = __kernel_getcpu();
     911                #endif
     912        }
     913
     914        #if defined(USE_CPU_WORK_STEALING)
     915                /* paranoid */ verify(pref >= 0);
     916                /* paranoid */ verify(pref < cpu_info.hthrd_count);
     917        #endif
    251918
    252919        return pref;
     920}
     921
     922//-----------------------------------------------------------------------
     923// Check that all the intrusive queues in the data structure are still consistent
     924static void check( __ready_queue_t & q ) with (q) {
     925        #if defined(__CFA_WITH_VERIFY__)
     926                {
     927                        for( idx ; lanes.count ) {
     928                                __intrusive_lane_t & sl = lanes.data[idx];
     929                                assert(!lanes.data[idx].lock);
     930
     931                                        if(is_empty(sl)) {
     932                                                assert( sl.anchor.next == 0p );
     933                                                assert( sl.anchor.ts   == -1llu );
     934                                                assert( mock_head(sl)  == sl.prev );
     935                                        } else {
     936                                                assert( sl.anchor.next != 0p );
     937                                                assert( sl.anchor.ts   != -1llu );
     938                                                assert( mock_head(sl)  != sl.prev );
     939                                        }
     940                        }
     941                }
     942        #endif
    253943}
    254944
    255945//-----------------------------------------------------------------------
    256946// Given 2 indexes, pick the list with the oldest push an try to pop from it
    257 static inline struct thread$ * try_pop(struct cluster * cltr, unsigned i, unsigned j __STATS(, __stats_readyQ_pop_t & stats)) with (cltr->sched) {
     947static inline struct thread$ * try_pop(struct cluster * cltr, unsigned i, unsigned j __STATS(, __stats_readyQ_pop_t & stats)) with (cltr->ready_queue) {
    258948        // Pick the bet list
    259949        int w = i;
    260         if( __builtin_expect(!is_empty(readyQ.data[j]), true) ) {
    261                 w = (ts(readyQ.data[i]) < ts(readyQ.data[j])) ? i : j;
     950        if( __builtin_expect(!is_empty(lanes.data[j]), true) ) {
     951                w = (ts(lanes.data[i]) < ts(lanes.data[j])) ? i : j;
    262952        }
    263953
    264954        return try_pop(cltr, w __STATS(, stats));
    265955}
     956
     957// Call this function of the intrusive list was moved using memcpy
     958// fixes the list so that the pointers back to anchors aren't left dangling
     959static inline void fix(__intrusive_lane_t & ll) {
     960                        if(is_empty(ll)) {
     961                                verify(ll.anchor.next == 0p);
     962                                ll.prev = mock_head(ll);
     963                        }
     964}
     965
     966static void assign_list(unsigned & value, dlist(processor) & list, unsigned count) {
     967        processor * it = &list`first;
     968        for(unsigned i = 0; i < count; i++) {
     969                /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count);
     970                it->rdq.id = value;
     971                it->rdq.target = MAX;
     972                value += READYQ_SHARD_FACTOR;
     973                it = &(*it)`next;
     974        }
     975}
     976
     977static void reassign_cltr_id(struct cluster * cltr) {
     978        unsigned preferred = 0;
     979        assign_list(preferred, cltr->procs.actives, cltr->procs.total - cltr->procs.idle);
     980        assign_list(preferred, cltr->procs.idles  , cltr->procs.idle );
     981}
     982
     983static void fix_times( struct cluster * cltr ) with( cltr->ready_queue ) {
     984        #if defined(USE_AWARE_STEALING) || defined(USE_WORK_STEALING)
     985                lanes.tscs = alloc(lanes.count, lanes.tscs`realloc);
     986                for(i; lanes.count) {
     987                        lanes.tscs[i].tv = rdtscl();
     988                        lanes.tscs[i].ma = 0;
     989                }
     990        #endif
     991}
     992
     993#if defined(USE_CPU_WORK_STEALING)
     994        // ready_queue size is fixed in this case
     995        void ready_queue_grow(struct cluster * cltr) {}
     996        void ready_queue_shrink(struct cluster * cltr) {}
     997#else
     998        // Grow the ready queue
     999        void ready_queue_grow(struct cluster * cltr) {
     1000                size_t ncount;
     1001                int target = cltr->procs.total;
     1002
     1003                /* paranoid */ verify( ready_mutate_islocked() );
     1004                __cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue\n");
     1005
     1006                // Make sure that everything is consistent
     1007                /* paranoid */ check( cltr->ready_queue );
     1008
     1009                // grow the ready queue
     1010                with( cltr->ready_queue ) {
     1011                        // Find new count
     1012                        // Make sure we always have atleast 1 list
     1013                        if(target >= 2) {
     1014                                ncount = target * READYQ_SHARD_FACTOR;
     1015                        } else {
     1016                                ncount = SEQUENTIAL_SHARD;
     1017                        }
     1018
     1019                        // Allocate new array (uses realloc and memcpies the data)
     1020                        lanes.data = alloc( ncount, lanes.data`realloc );
     1021
     1022                        // Fix the moved data
     1023                        for( idx; (size_t)lanes.count ) {
     1024                                fix(lanes.data[idx]);
     1025                        }
     1026
     1027                        // Construct new data
     1028                        for( idx; (size_t)lanes.count ~ ncount) {
     1029                                (lanes.data[idx]){};
     1030                        }
     1031
     1032                        // Update original
     1033                        lanes.count = ncount;
     1034
     1035                        lanes.caches = alloc( target, lanes.caches`realloc );
     1036                }
     1037
     1038                fix_times(cltr);
     1039
     1040                reassign_cltr_id(cltr);
     1041
     1042                // Make sure that everything is consistent
     1043                /* paranoid */ check( cltr->ready_queue );
     1044
     1045                __cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue done\n");
     1046
     1047                /* paranoid */ verify( ready_mutate_islocked() );
     1048        }
     1049
     1050        // Shrink the ready queue
     1051        void ready_queue_shrink(struct cluster * cltr) {
     1052                /* paranoid */ verify( ready_mutate_islocked() );
     1053                __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue\n");
     1054
     1055                // Make sure that everything is consistent
     1056                /* paranoid */ check( cltr->ready_queue );
     1057
     1058                int target = cltr->procs.total;
     1059
     1060                with( cltr->ready_queue ) {
     1061                        // Remember old count
     1062                        size_t ocount = lanes.count;
     1063
     1064                        // Find new count
     1065                        // Make sure we always have atleast 1 list
     1066                        lanes.count = target >= 2 ? target * READYQ_SHARD_FACTOR: SEQUENTIAL_SHARD;
     1067                        /* paranoid */ verify( ocount >= lanes.count );
     1068                        /* paranoid */ verify( lanes.count == target * READYQ_SHARD_FACTOR || target < 2 );
     1069
     1070                        // for printing count the number of displaced threads
     1071                        #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
     1072                                __attribute__((unused)) size_t displaced = 0;
     1073                        #endif
     1074
     1075                        // redistribute old data
     1076                        for( idx; (size_t)lanes.count ~ ocount) {
     1077                                // Lock is not strictly needed but makes checking invariants much easier
     1078                                __attribute__((unused)) bool locked = __atomic_try_acquire(&lanes.data[idx].lock);
     1079                                verify(locked);
     1080
     1081                                // As long as we can pop from this lane to push the threads somewhere else in the queue
     1082                                while(!is_empty(lanes.data[idx])) {
     1083                                        struct thread$ * thrd;
     1084                                        unsigned long long _;
     1085                                        [thrd, _] = pop(lanes.data[idx]);
     1086
     1087                                        push(cltr, thrd, true);
     1088
     1089                                        // for printing count the number of displaced threads
     1090                                        #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
     1091                                                displaced++;
     1092                                        #endif
     1093                                }
     1094
     1095                                // Unlock the lane
     1096                                __atomic_unlock(&lanes.data[idx].lock);
     1097
     1098                                // TODO print the queue statistics here
     1099
     1100                                ^(lanes.data[idx]){};
     1101                        }
     1102
     1103                        __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue displaced %zu threads\n", displaced);
     1104
     1105                        // Allocate new array (uses realloc and memcpies the data)
     1106                        lanes.data = alloc( lanes.count, lanes.data`realloc );
     1107
     1108                        // Fix the moved data
     1109                        for( idx; (size_t)lanes.count ) {
     1110                                fix(lanes.data[idx]);
     1111                        }
     1112
     1113                        lanes.caches = alloc( target, lanes.caches`realloc );
     1114                }
     1115
     1116                fix_times(cltr);
     1117
     1118
     1119                reassign_cltr_id(cltr);
     1120
     1121                // Make sure that everything is consistent
     1122                /* paranoid */ check( cltr->ready_queue );
     1123
     1124                __cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue done\n");
     1125                /* paranoid */ verify( ready_mutate_islocked() );
     1126        }
     1127#endif
     1128
     1129#if !defined(__CFA_NO_STATISTICS__)
     1130        unsigned cnt(const __ready_queue_t & this, unsigned idx) {
     1131                /* paranoid */ verify(this.lanes.count > idx);
     1132                return this.lanes.data[idx].cnt;
     1133        }
     1134#endif
     1135
     1136
     1137#if   defined(CFA_HAVE_LINUX_LIBRSEQ)
     1138        // No definition needed
     1139#elif defined(CFA_HAVE_LINUX_RSEQ_H)
     1140
     1141        #if defined( __x86_64 ) || defined( __i386 )
     1142                #define RSEQ_SIG        0x53053053
     1143        #elif defined( __ARM_ARCH )
     1144                #ifdef __ARMEB__
     1145                #define RSEQ_SIG    0xf3def5e7      /* udf    #24035    ; 0x5de3 (ARMv6+) */
     1146                #else
     1147                #define RSEQ_SIG    0xe7f5def3      /* udf    #24035    ; 0x5de3 */
     1148                #endif
     1149        #endif
     1150
     1151        extern void __disable_interrupts_hard();
     1152        extern void __enable_interrupts_hard();
     1153
     1154        static void __kernel_raw_rseq_register  (void) {
     1155                /* paranoid */ verify( __cfaabi_rseq.cpu_id == RSEQ_CPU_ID_UNINITIALIZED );
     1156
     1157                // int ret = syscall(__NR_rseq, &__cfaabi_rseq, sizeof(struct rseq), 0, (sigset_t *)0p, _NSIG / 8);
     1158                int ret = syscall(__NR_rseq, &__cfaabi_rseq, sizeof(struct rseq), 0, RSEQ_SIG);
     1159                if(ret != 0) {
     1160                        int e = errno;
     1161                        switch(e) {
     1162                        case EINVAL: abort("KERNEL ERROR: rseq register invalid argument");
     1163                        case ENOSYS: abort("KERNEL ERROR: rseq register no supported");
     1164                        case EFAULT: abort("KERNEL ERROR: rseq register with invalid argument");
     1165                        case EBUSY : abort("KERNEL ERROR: rseq register already registered");
     1166                        case EPERM : abort("KERNEL ERROR: rseq register sig  argument  on unregistration does not match the signature received on registration");
     1167                        default: abort("KERNEL ERROR: rseq register unexpected return %d", e);
     1168                        }
     1169                }
     1170        }
     1171
     1172        static void __kernel_raw_rseq_unregister(void) {
     1173                /* paranoid */ verify( __cfaabi_rseq.cpu_id >= 0 );
     1174
     1175                // int ret = syscall(__NR_rseq, &__cfaabi_rseq, sizeof(struct rseq), RSEQ_FLAG_UNREGISTER, (sigset_t *)0p, _NSIG / 8);
     1176                int ret = syscall(__NR_rseq, &__cfaabi_rseq, sizeof(struct rseq), RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
     1177                if(ret != 0) {
     1178                        int e = errno;
     1179                        switch(e) {
     1180                        case EINVAL: abort("KERNEL ERROR: rseq unregister invalid argument");
     1181                        case ENOSYS: abort("KERNEL ERROR: rseq unregister no supported");
     1182                        case EFAULT: abort("KERNEL ERROR: rseq unregister with invalid argument");
     1183                        case EBUSY : abort("KERNEL ERROR: rseq unregister already registered");
     1184                        case EPERM : abort("KERNEL ERROR: rseq unregister sig  argument  on unregistration does not match the signature received on registration");
     1185                        default: abort("KERNEL ERROR: rseq unregisteunexpected return %d", e);
     1186                        }
     1187                }
     1188        }
     1189#else
     1190        // No definition needed
     1191#endif
  • libcfa/src/concurrency/ready_subqueue.hfa

    r92538ab r4559b34  
    33#define __CFA_NO_SCHED_STATS__
    44
    5 #include "limits.hfa"
     5#include "containers/queueLockFree.hfa"
    66
    77// Intrusives lanes which are used by the relaxed ready queue
     
    2727}
    2828
     29// Ctor
     30void ?{}( __intrusive_lane_t & this ) {
     31        this.lock = false;
     32        this.prev = mock_head(this);
     33        this.anchor.next = 0p;
     34        this.anchor.ts   = -1llu;
     35        #if !defined(__CFA_NO_STATISTICS__)
     36                this.cnt  = 0;
     37        #endif
     38
     39        // We add a boat-load of assertions here because the anchor code is very fragile
     40        /* paranoid */ _Static_assert( offsetof( thread$, link ) == offsetof(__intrusive_lane_t, anchor) );
     41        /* paranoid */ verify( offsetof( thread$, link ) == offsetof(__intrusive_lane_t, anchor) );
     42        /* paranoid */ verify( ((uintptr_t)( mock_head(this) ) + offsetof( thread$, link )) == (uintptr_t)(&this.anchor) );
     43        /* paranoid */ verify( &mock_head(this)->link.next == &this.anchor.next );
     44        /* paranoid */ verify( &mock_head(this)->link.ts   == &this.anchor.ts   );
     45        /* paranoid */ verify( mock_head(this)->link.next == 0p );
     46        /* paranoid */ verify( mock_head(this)->link.ts   == -1llu  );
     47        /* paranoid */ verify( mock_head(this) == this.prev );
     48        /* paranoid */ verify( __alignof__(__intrusive_lane_t) == 128 );
     49        /* paranoid */ verify( __alignof__(this) == 128 );
     50        /* paranoid */ verifyf( ((intptr_t)(&this) % 128) == 0, "Expected address to be aligned %p %% 128 == %zd", &this, ((intptr_t)(&this) % 128) );
     51}
     52
     53// Dtor is trivial
     54void ^?{}( __intrusive_lane_t & this ) {
     55        // Make sure the list is empty
     56        /* paranoid */ verify( this.anchor.next == 0p );
     57        /* paranoid */ verify( this.anchor.ts   == -1llu );
     58        /* paranoid */ verify( mock_head(this)  == this.prev );
     59}
     60
    2961// Push a thread onto this lane
    3062// returns true of lane was empty before push, false otherwise
     
    3264        /* paranoid */ verify( this.lock );
    3365        /* paranoid */ verify( node->link.next == 0p );
    34         /* paranoid */ verify( node->link.ts   == MAX  );
     66        /* paranoid */ verify( node->link.ts   == -1llu  );
    3567        /* paranoid */ verify( this.prev->link.next == 0p );
    36         /* paranoid */ verify( this.prev->link.ts   == MAX  );
     68        /* paranoid */ verify( this.prev->link.ts   == -1llu  );
    3769        if( this.anchor.next == 0p ) {
    3870                /* paranoid */ verify( this.anchor.next == 0p );
    39                 /* paranoid */ verify( this.anchor.ts   == MAX );
     71                /* paranoid */ verify( this.anchor.ts   == -1llu );
    4072                /* paranoid */ verify( this.anchor.ts   != 0  );
    4173                /* paranoid */ verify( this.prev == mock_head( this ) );
    4274        } else {
    4375                /* paranoid */ verify( this.anchor.next != 0p );
    44                 /* paranoid */ verify( this.anchor.ts   != MAX );
     76                /* paranoid */ verify( this.anchor.ts   != -1llu );
    4577                /* paranoid */ verify( this.anchor.ts   != 0  );
    4678                /* paranoid */ verify( this.prev != mock_head( this ) );
     
    6294        /* paranoid */ verify( this.lock );
    6395        /* paranoid */ verify( this.anchor.next != 0p );
    64         /* paranoid */ verify( this.anchor.ts   != MAX );
     96        /* paranoid */ verify( this.anchor.ts   != -1llu );
    6597        /* paranoid */ verify( this.anchor.ts   != 0  );
    6698
     
    71103        bool is_empty = this.anchor.next == 0p;
    72104        node->link.next = 0p;
    73         node->link.ts   = MAX;
     105        node->link.ts   = -1llu;
    74106        #if !defined(__CFA_NO_STATISTICS__)
    75107                this.cnt--;
     
    80112
    81113        /* paranoid */ verify( node->link.next == 0p );
    82         /* paranoid */ verify( node->link.ts   == MAX  );
     114        /* paranoid */ verify( node->link.ts   == -1llu  );
    83115        /* paranoid */ verify( node->link.ts   != 0  );
    84116        /* paranoid */ verify( this.anchor.ts  != 0  );
  • libcfa/src/concurrency/stats.cfa

    r92538ab r4559b34  
    5555                        stats->io.calls.drain       = 0;
    5656                        stats->io.calls.completed   = 0;
    57                         stats->io.calls.locked      = 0;
    58                         stats->io.calls.helped      = 0;
    5957                        stats->io.calls.errors.busy = 0;
    6058                        stats->io.ops.sockread      = 0;
     
    125123                        tally_one( &cltr->io.calls.drain      , &proc->io.calls.drain       );
    126124                        tally_one( &cltr->io.calls.completed  , &proc->io.calls.completed   );
    127                         tally_one( &cltr->io.calls.locked     , &proc->io.calls.locked      );
    128                         tally_one( &cltr->io.calls.helped     , &proc->io.calls.helped      );
    129125                        tally_one( &cltr->io.calls.errors.busy, &proc->io.calls.errors.busy );
    130126                        tally_one( &cltr->io.ops.sockread     , &proc->io.ops.sockread      );
     
    209205                                     |   " sub " | eng3(io.calls.submitted) | "/" | eng3(io.calls.flush) | "(" | ws(3, 3, avgsubs) | "/flush)"
    210206                                     | " - cmp " | eng3(io.calls.completed) | "/" | eng3(io.calls.drain) | "(" | ws(3, 3, avgcomp) | "/drain)"
    211                                      | " - cmp " | eng3(io.calls.locked) | "locked, " | eng3(io.calls.helped) | "helped"
    212207                                     | " - " | eng3(io.calls.errors.busy) | " EBUSY";
    213208                                sstr | " - sub: " | eng3(io.flush.full) | "full, " | eng3(io.flush.dirty) | "drty, " | eng3(io.flush.idle) | "idle, " | eng3(io.flush.eager) | "eagr, " | eng3(io.flush.external) | "ext";
  • libcfa/src/concurrency/stats.hfa

    r92538ab r4559b34  
    103103                                volatile uint64_t drain;
    104104                                volatile uint64_t completed;
    105                                 volatile uint64_t locked;
    106                                 volatile uint64_t helped;
    107105                                volatile uint64_t flush;
    108106                                volatile uint64_t submitted;
  • libcfa/src/concurrency/thread.cfa

    r92538ab r4559b34  
    1010// Created On       : Tue Jan 17 12:27:26 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Feb 12 15:24:18 2022
    13 // Update Count     : 66
     12// Last Modified On : Sat Jan 15 14:34:58 2022
     13// Update Count     : 45
    1414//
    1515
     
    1919#include "thread.hfa"
    2020
    21 #include "kernel/private.hfa"
     21#include "kernel_private.hfa"
    2222#include "exception.hfa"
    2323
     
    2525#include "invoke.h"
    2626
    27 extern uint32_t __global_random_seed, __global_random_prime, __global_random_mask;
     27extern uint32_t __global_random_seed;
    2828
    2929//-----------------------------------------------------------------------------
     
    4545        preferred = ready_queue_new_preferred();
    4646        last_proc = 0p;
    47         random_state = __global_random_mask ? __global_random_prime : __global_random_prime ^ rdtscl();
     47        random_state = __global_random_seed;
    4848        #if defined( __CFA_WITH_VERIFY__ )
    4949                canary = 0x0D15EA5E0D15EA5Ep;
     
    176176
    177177void set_seed( uint32_t seed ) {
    178         uint32_t & state = active_thread()->random_state;
    179         state = __global_random_seed = seed;
    180         GENERATOR( state );
    181         __global_random_prime = state;
    182         __global_random_mask = true;
     178        active_thread()->random_state = __global_random_seed = seed;
     179        GENERATOR( active_thread()->random_state );
    183180} // set_seed
    184181
  • libcfa/src/concurrency/thread.hfa

    r92538ab r4559b34  
    1010// Created On       : Tue Jan 17 12:27:26 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Feb 11 16:34:07 2022
    13 // Update Count     : 20
     12// Last Modified On : Wed Feb  9 22:10:14 2022
     13// Update Count     : 14
    1414//
    1515
     
    131131
    132132//----------
    133 // prng
    134133static inline {
    135134        uint32_t prng( thread$ & th ) __attribute__(( warn_unused_result )) { return LCG( th.random_state ); } // [0,UINT_MAX]
    136135        uint32_t prng( thread$ & th, uint32_t u ) __attribute__(( warn_unused_result )) { return prng( th ) % u; } // [0,u)
    137136        uint32_t prng( thread$ & th, uint32_t l, uint32_t u ) __attribute__(( warn_unused_result )) { return prng( th, u - l + 1 ) + l; } // [l,u]
    138         forall( T & | is_thread(T) ) {
    139                 uint32_t prng( T & th ) __attribute__(( warn_unused_result )) { return prng( (thread &)th ); } // [0,UINT_MAX]
    140                 uint32_t prng( T & th, uint32_t u ) __attribute__(( warn_unused_result )) { return prng( th ) % u; } // [0,u)
    141                 uint32_t prng( T & th, uint32_t l, uint32_t u ) __attribute__(( warn_unused_result )) { return prng( th, u - l + 1 ) + l; } // [l,u]
    142         } // distribution
    143 } // distribution
     137} // prng
    144138
    145139// Local Variables: //
  • libcfa/src/containers/string.cfa

    r92538ab r4559b34  
    9292}
    9393
    94 string & ?=?(string & this, string & other) { //// <---- straw man change
     94string ?=?(string & this, string other) {
    9595    (*this.inner) = (*other.inner);
    9696    return this;
     
    235235int find(const string &s, const char* search, size_t searchsize) {
    236236    return find( *s.inner, search, searchsize);
    237 }
    238 
    239 int findFrom(const string &s, size_t fromPos, char search) {
    240     return findFrom( *s.inner, fromPos, search );
    241 }
    242 
    243 int findFrom(const string &s, size_t fromPos, const string &search) {
    244     return findFrom( *s.inner, fromPos, *search.inner );
    245 }
    246 
    247 int findFrom(const string &s, size_t fromPos, const char* search) {
    248     return findFrom( *s.inner, fromPos, search );
    249 }
    250 
    251 int findFrom(const string &s, size_t fromPos, const char* search, size_t searchsize) {
    252     return findFrom( *s.inner, fromPos, search, searchsize );
    253237}
    254238
  • libcfa/src/containers/string.hfa

    r92538ab r4559b34  
    4141void ?=?(string &s, const string &other);
    4242void ?=?(string &s, char other);
    43 string & ?=?(string &s, string &other);  // surprising ret seems to help avoid calls to autogen
    44 //string ?=?( string &, string ) = void;
     43string ?=?(string &s, string other);  // string tolerates memcpys; still saw calls to autogen
     44
    4545void ^?{}(string &s);
    4646
     
    9393int find(const string &s, const char* search, size_t searchsize);
    9494
    95 int findFrom(const string &s, size_t fromPos, char search);
    96 int findFrom(const string &s, size_t fromPos, const string &search);
    97 int findFrom(const string &s, size_t fromPos, const char* search);
    98 int findFrom(const string &s, size_t fromPos, const char* search, size_t searchsize);
    99 
    10095bool includes(const string &s, const string &search);
    10196bool includes(const string &s, const char* search);
  • libcfa/src/containers/string_res.cfa

    r92538ab r4559b34  
    1515
    1616#include "string_res.hfa"
    17 #include "string_sharectx.hfa"
    18 #include "stdlib.hfa"
    19 
    20 // Workaround for observed performance penalty from calling CFA's alloc.
    21 // Workaround is:  EndVbyte = TEMP_ALLOC(char, CurrSize)
    22 // Should be:      EndVbyte = alloc(CurrSize)
    23 #define TEMP_ALLOC(T, n) (( T* ) malloc( n * sizeof( T ) ))
    24 
    25 #include <assert.h>
     17#include <stdlib.hfa>  // e.g. malloc
     18#include <string.h>    // e.g. strlen
    2619
    2720//######################### VbyteHeap "header" #########################
     21
     22
     23
     24
     25
     26
     27
     28
     29// DON'T COMMIT:
     30// #define VbyteDebug
     31
     32
     33
     34
    2835
    2936#ifdef VbyteDebug
     
    4754
    4855   
    49 static void compaction( VbyteHeap & );                          // compaction of the byte area
    50 static void garbage( VbyteHeap &, int );                                // garbage collect the byte area
    51 static void extend( VbyteHeap &, int );                 // extend the size of the byte area
    52 static void reduce( VbyteHeap &, int );                 // reduce the size of the byte area
    53 
    54 static void ?{}( VbyteHeap &, size_t = 1000 );
    55 static void ^?{}( VbyteHeap & );
    56 
    57 static int ByteCmp( char *, int, int, char *, int, int );       // compare 2 blocks of bytes
    58 static char *VbyteAlloc( VbyteHeap &, int );                    // allocate a block bytes in the heap
    59 static char *VbyteTryAdjustLast( VbyteHeap &, int );
    60 
    61 static void AddThisAfter( HandleNode &, HandleNode & );
    62 static void DeleteNode( HandleNode & );
    63 static void MoveThisAfter( HandleNode &, const HandleNode & );          // move current handle after parameter handle
     56static inline void compaction( VbyteHeap & );                           // compaction of the byte area
     57static inline void garbage( VbyteHeap & );                              // garbage collect the byte area
     58static inline void extend( VbyteHeap &, int );                  // extend the size of the byte area
     59static inline void reduce( VbyteHeap &, int );                  // reduce the size of the byte area
     60
     61static inline void ?{}( VbyteHeap &, int = 1000 );
     62static inline void ^?{}( VbyteHeap & );
     63static inline void ByteCopy( VbyteHeap &, char *, int, int, char *, int, int ); // copy a block of bytes from one location in the heap to another
     64static inline int ByteCmp( VbyteHeap &, char *, int, int, char *, int, int );   // compare 2 blocks of bytes
     65static inline char *VbyteAlloc( VbyteHeap &, int );                     // allocate a block bytes in the heap
     66
     67
     68static inline void AddThisAfter( HandleNode &, HandleNode & );
     69static inline void DeleteNode( HandleNode & );
     70static inline void MoveThisAfter( HandleNode &, const HandleNode & );           // move current handle after parameter handle
    6471
    6572
    6673// Allocate the storage for the variable sized area and intialize the heap variables.
    6774
    68 static void ?{}( VbyteHeap & this, size_t Size ) with(this) {
     75static inline void ?{}( VbyteHeap & this, int Size ) with(this) {
    6976#ifdef VbyteDebug
    7077    serr | "enter:VbyteHeap::VbyteHeap, this:" | &this | " Size:" | Size;
     
    7279    NoOfCompactions = NoOfExtensions = NoOfReductions = 0;
    7380    InitSize = CurrSize = Size;
    74     StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     81    StartVbyte = EndVbyte = alloc(CurrSize);
    7582    ExtVbyte = (void *)( StartVbyte + CurrSize );
    7683    Header.flink = Header.blink = &Header;
    77     Header.ulink = & this;
    7884#ifdef VbyteDebug
    7985    HeaderPtr = &Header;
     
    8591// Release the dynamically allocated storage for the byte area.
    8692
    87 static void ^?{}( VbyteHeap & this ) with(this) {
     93static inline void ^?{}( VbyteHeap & this ) with(this) {
    8894    free( StartVbyte );
    8995} // ~VbyteHeap
     
    96102// creator.
    97103
    98 static void ?{}( HandleNode & this ) with(this) {
     104void ?{}( HandleNode & this ) with(this) {
    99105#ifdef VbyteDebug
    100106    serr | "enter:HandleNode::HandleNode, this:" | &this;
     
    111117// collection.
    112118
    113 static void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) {
     119void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) {
    114120#ifdef VbyteDebug
    115121    serr | "enter:HandleNode::HandleNode, this:" | &this;
     
    117123    s = 0;
    118124    lnth = 0;
    119     ulink = &vh;
    120125    AddThisAfter( this, *vh.Header.blink );
    121126#ifdef VbyteDebug
     
    128133// is the responsibility of the creator to destroy it.
    129134
    130 static void ^?{}( HandleNode & this ) with(this) {
     135void ^?{}( HandleNode & this ) with(this) {
    131136#ifdef VbyteDebug
    132137    serr | "enter:HandleNode::~HandleNode, this:" | & this;
     
    144149} // ~HandleNode
    145150
    146 
    147 //######################### String Sharing Context #########################
    148 
    149 static string_sharectx * ambient_string_sharectx;               // fickle top of stack
    150 static string_sharectx default_string_sharectx = {NEW_SHARING}; // stable bottom of stack
    151 
    152 void ?{}( string_sharectx & this, StringSharectx_Mode mode ) with( this ) {
    153     (older){ ambient_string_sharectx };
    154     if ( mode == NEW_SHARING ) {
    155         (activeHeap){ new( (size_t) 1000 ) };
    156     } else {
    157         verify( mode == NO_SHARING );
    158         (activeHeap){ 0p };
    159     }
    160     ambient_string_sharectx = & this;
    161 }
    162 
    163 void ^?{}( string_sharectx & this ) with( this ) {
    164     if ( activeHeap ) delete( activeHeap );
    165 
    166     // unlink this from older-list starting from ambient_string_sharectx
    167     // usually, this==ambient_string_sharectx and the loop runs zero times
    168     string_sharectx *& c = ambient_string_sharectx;
    169     while ( c != &this ) &c = &c->older;              // find this
    170     c = this.older;                                   // unlink
    171 }
    172 
    173151//######################### String Resource #########################
    174152
    175153
    176 VbyteHeap * DEBUG_string_heap() {
    177     assert( ambient_string_sharectx->activeHeap && "No sharing context is active" );
    178     return ambient_string_sharectx->activeHeap;
    179 }
     154VbyteHeap HeapArea;
     155
     156VbyteHeap * DEBUG_string_heap = & HeapArea;
    180157
    181158size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap ) {
     
    183160}
    184161
    185 size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap ) {
    186     return heap->CurrSize;
    187 }
    188 
    189162const char * DEBUG_string_heap_start( VbyteHeap * heap ) {
    190163    return heap->StartVbyte;
    191164}
     165
    192166
    193167// Returns the size of the string in bytes
     
    213187    // Store auto-newline state so it can be restored
    214188    bool anl = getANL$(out);
    215     if( s.Handle.lnth == 0 ) {
    216         sout | "";
    217     } else {
    218         nlOff(out);
    219         for (size_t i = 0; i < s.Handle.lnth; i++) {
    220             // Need to re-apply on the last output operator, for whole-statement version
    221             if (anl && i == s.Handle.lnth-1) nlOn(out);
    222             out | s[i];
    223         }
    224     }
     189    nlOff(out);
     190    for (size_t i = 0; i < s.Handle.lnth; i++) {
     191        // Need to re-apply on the last output operator, for whole-statement version
     192        if (anl && i == s.Handle.lnth-1) nlOn(out);
     193        out | s[i];
     194    }
     195    return out;
    225196}
    226197
    227198// Empty constructor
    228199void ?{}(string_res &s) with(s) {
    229     if( ambient_string_sharectx->activeHeap ) {
    230         (Handle){ * ambient_string_sharectx->activeHeap };
    231         (shareEditSet_owns_ulink){ false };
    232         verify( Handle.s == 0p && Handle.lnth == 0 );
    233     } else {
    234         (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
    235         (shareEditSet_owns_ulink){ true };
    236         Handle.s = Handle.ulink->StartVbyte;
    237         verify( Handle.lnth == 0 );
    238     }
     200    (Handle){ HeapArea };
    239201    s.shareEditSet_prev = &s;
    240202    s.shareEditSet_next = &s;
    241203}
    242204
    243 static void eagerCopyCtorHelper(string_res &s, const char* rhs, size_t rhslnth) with(s) {
    244     if( ambient_string_sharectx->activeHeap ) {
    245         (Handle){ * ambient_string_sharectx->activeHeap };
    246         (shareEditSet_owns_ulink){ false };
    247     } else {
    248         (Handle){ * new( rhslnth ) };
    249         (shareEditSet_owns_ulink){ true };
    250     }
    251     Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     205// Constructor from a raw buffer and size
     206void ?{}(string_res &s, const char* rhs, size_t rhslnth) with(s) {
     207    (Handle){ HeapArea };
     208    Handle.s = VbyteAlloc(HeapArea, rhslnth);
    252209    Handle.lnth = rhslnth;
    253     memmove( Handle.s, rhs, rhslnth );
     210    for ( int i = 0; i < rhslnth; i += 1 ) {            // copy characters
     211        Handle.s[i] = rhs[i];
     212    } // for
    254213    s.shareEditSet_prev = &s;
    255214    s.shareEditSet_next = &s;
    256215}
    257216
    258 // Constructor from a raw buffer and size
    259 void ?{}(string_res &s, const char* rhs, size_t rhslnth) with(s) {
    260     eagerCopyCtorHelper(s, rhs, rhslnth);
    261 }
    262 
    263 // private ctor (not in header): use specified heap (ignore ambient) and copy chars in
    264 void ?{}( string_res &s, VbyteHeap & heap, const char* rhs, size_t rhslnth ) with(s) {
    265     (Handle){ heap };
    266     Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
    267     Handle.lnth = rhslnth;
    268     (s.shareEditSet_owns_ulink){ false };
    269     memmove( Handle.s, rhs, rhslnth );
    270     s.shareEditSet_prev = &s;
    271     s.shareEditSet_next = &s;
     217// String literal constructor
     218void ?{}(string_res &s, const char* rhs) {
     219    (s){ rhs, strlen(rhs) };
    272220}
    273221
     
    275223void ?{}(string_res &s, const string_res & s2, StrResInitMode mode, size_t start, size_t end ) {
    276224
    277     verify( start <= end && end <= s2.Handle.lnth );
    278 
    279     if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) {
    280         // crossing heaps (including private): copy eagerly
    281         eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
    282         verify(s.shareEditSet_prev == &s);
    283         verify(s.shareEditSet_next == &s);
     225    (s.Handle){ HeapArea };
     226    s.Handle.s = s2.Handle.s + start;
     227    s.Handle.lnth = end - start;
     228    MoveThisAfter(s.Handle, s2.Handle );                        // insert this handle after rhs handle
     229    // ^ bug?  skip others at early point in string
     230   
     231    if (mode == COPY_VALUE) {
     232        // make s alone in its shareEditSet
     233        s.shareEditSet_prev = &s;
     234        s.shareEditSet_next = &s;
    284235    } else {
    285         (s.Handle){};
    286         s.Handle.s = s2.Handle.s + start;
    287         s.Handle.lnth = end - start;
    288         s.Handle.ulink = s2.Handle.ulink;
    289 
    290         AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
    291         // ^ bug?  skip others at early point in string
    292 
    293         if (mode == COPY_VALUE) {
    294             verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap);
    295             // requested logical copy in same heap: defer copy until write
    296 
    297             (s.shareEditSet_owns_ulink){ false };
    298 
    299             // make s alone in its shareEditSet
    300             s.shareEditSet_prev = &s;
    301             s.shareEditSet_next = &s;
    302         } else {
    303             verify( mode == SHARE_EDITS );
    304             // sharing edits with source forces same heap as source (ignore context)
    305 
    306             (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink };
    307 
    308             // s2 is logically const but not implementation const
    309             string_res & s2mod = (string_res &) s2;
    310 
    311             // insert s after s2 on shareEditSet
    312             s.shareEditSet_next = s2mod.shareEditSet_next;
    313             s.shareEditSet_prev = &s2mod;
    314             s.shareEditSet_next->shareEditSet_prev = &s;
    315             s.shareEditSet_prev->shareEditSet_next = &s;
     236        assert( mode == SHARE_EDITS );
     237
     238        // s2 is logically const but not implementation const
     239        string_res & s2mod = (string_res &) s2;
     240
     241        // insert s after s2 on shareEditSet
     242        s.shareEditSet_next = s2mod.shareEditSet_next;
     243        s.shareEditSet_prev = &s2mod;
     244        s.shareEditSet_next->shareEditSet_prev = &s;
     245        s.shareEditSet_prev->shareEditSet_next = &s;
     246    }
     247}
     248
     249void assign(string_res &this, const char* buffer, size_t bsize) {
     250
     251    // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs
     252    string_res * shareEditSetStartPeer = & this;
     253    string_res * shareEditSetEndPeer = & this;
     254    for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) {
     255        if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
     256            shareEditSetStartPeer = editPeer;
    316257        }
    317     }
    318 }
    319 
    320 static void assignEditSet(string_res & this, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer,
    321     char * resultSesStart,
    322     size_t resultSesLnth,
    323     HandleNode * resultPadPosition, size_t bsize ) {
     258        if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
     259            shareEditSetEndPeer = editPeer;
     260        }
     261    }
     262
     263    // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
     264    // `this` occurs in the middle of it, to be replaced
     265    // build up the new text in `pasting`
     266
     267    string_res pasting = {
     268        shareEditSetStartPeer->Handle.s,                   // start of SES
     269        this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this
     270    append( pasting,
     271        buffer,                                            // start of replacement for this
     272        bsize );                                           // length of replacement for this
     273    append( pasting,
     274        this.Handle.s + this.Handle.lnth,                  // start of SES after this
     275        shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
     276        (this.Handle.s + this.Handle.lnth) );              // length of SES, after this
     277
     278    // The above string building can trigger compaction.
     279    // The reference points (that are arguments of the string building) may move during that building.
     280    // From this point on, they are stable.
     281    // So now, capture their values for use in the overlap cases, below.
     282    // Do not factor these definitions with the arguments used above.
    324283
    325284    char * beforeBegin = shareEditSetStartPeer->Handle.s;
     
    331290    size_t oldLnth = this.Handle.lnth;
    332291
    333     this.Handle.s = resultSesStart + beforeLen;
     292    this.Handle.s = pasting.Handle.s + beforeLen;
    334293    this.Handle.lnth = bsize;
    335     if (resultPadPosition)
    336         MoveThisAfter( this.Handle, *resultPadPosition );
     294    MoveThisAfter( this.Handle, pasting.Handle );
    337295
    338296    // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
    339     char *limit = resultSesStart + resultSesLnth;
     297    char *limit = pasting.Handle.s + pasting.Handle.lnth;
    340298    for (string_res * p = this.shareEditSet_next; p != &this; p = p->shareEditSet_next) {
    341         verify (p->Handle.s >= beforeBegin);
     299        assert (p->Handle.s >= beforeBegin);
    342300        if ( p->Handle.s >= afterBegin ) {
    343             verify ( p->Handle.s <= afterBegin + afterLen );
    344             verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     301            assert ( p->Handle.s <= afterBegin + afterLen );
     302            assert ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    345303            // p starts after the edit
    346304            // take start and end as end-anchored
     
    360318            } else {
    361319                // p ends after the edit
    362                 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     320                assert ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    363321                // take end as end-anchored
    364322                // stretch-shrink p according to the edit
     
    368326            // take start as start-anchored
    369327            size_t startOffsetFromStart = p->Handle.s - beforeBegin;
    370             p->Handle.s = resultSesStart + startOffsetFromStart;
     328            p->Handle.s = pasting.Handle.s + startOffsetFromStart;
    371329        } else {
    372             verify ( p->Handle.s < afterBegin );
     330            assert ( p->Handle.s < afterBegin );
    373331            // p starts during the edit
    374             verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
     332            assert( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
    375333            if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
    376334                // p ends during the edit; p does not include the last character replaced
     
    386344            }
    387345        }
    388         if (resultPadPosition)
    389             MoveThisAfter( p->Handle, *resultPadPosition );     // move substring handle to maintain sorted order by string position
    390     }
    391 }
    392 
    393 static string_res & assign_(string_res &this, const char* buffer, size_t bsize, const string_res & valSrc) {
    394 
    395     // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs
    396     string_res * shareEditSetStartPeer = & this;
    397     string_res * shareEditSetEndPeer = & this;
    398     for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) {
    399         if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
    400             shareEditSetStartPeer = editPeer;
    401         }
    402         if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
    403             shareEditSetEndPeer = editPeer;
    404         }
    405     }
    406 
    407     verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
    408     size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
    409     verify( origEditSetLength >= this.Handle.lnth );
    410 
    411     if ( this.shareEditSet_owns_ulink ) {                 // assigning to private context
    412         // ok to overwrite old value within LHS
    413         char * prefixStartOrig = shareEditSetStartPeer->Handle.s;
    414         int prefixLen = this.Handle.s - prefixStartOrig;
    415         char * suffixStartOrig = this.Handle.s + this.Handle.lnth;
    416         int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig;
    417 
    418         int delta = bsize - this.Handle.lnth;
    419         if ( char * oldBytes = VbyteTryAdjustLast( *this.Handle.ulink, delta ) ) {
    420             // growing: copy from old to new
    421             char * dest = VbyteAlloc( *this.Handle.ulink, origEditSetLength + delta );
    422             char *destCursor = dest;  memcpy(destCursor, prefixStartOrig, prefixLen);
    423             destCursor += prefixLen;  memcpy(destCursor, buffer         , bsize    );
    424             destCursor += bsize;      memcpy(destCursor, suffixStartOrig, suffixLen);
    425             assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
    426                 dest,
    427                 origEditSetLength + delta,
    428                 0p, bsize);
    429             free( oldBytes );
    430         } else {
    431             // room is already allocated in-place: bubble suffix and overwite middle
    432             memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen );
    433             memcpy( this.Handle.s, buffer, bsize );
    434 
    435             assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
    436                 shareEditSetStartPeer->Handle.s,
    437                 origEditSetLength + delta,
    438                 0p, bsize);
    439         }
    440 
    441     } else if (                                           // assigning to shared context
    442         this.Handle.lnth == origEditSetLength &&          // overwriting entire run of SES
    443         & valSrc &&                                       // sourcing from a managed string
    444         valSrc.Handle.ulink == this.Handle.ulink  ) {     // sourcing from same heap
    445 
    446         // SES's result will only use characters from the source string => reuse source
    447         assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
    448             valSrc.Handle.s,
    449             valSrc.Handle.lnth,
    450             &((string_res&)valSrc).Handle, bsize);
    451        
    452     } else {
    453         // overwriting a proper substring of some string: mash characters from old and new together (copy on write)
    454         // OR we are importing characters: need to copy eagerly (can't refer to source)
    455 
    456         // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
    457         // `this` occurs in the middle of it, to be replaced
    458         // build up the new text in `pasting`
    459 
    460         string_res pasting = {
    461             * this.Handle.ulink,                               // maintain same heap, regardless of context
    462             shareEditSetStartPeer->Handle.s,                   // start of SES
    463             this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this
    464         append( pasting,
    465             buffer,                                            // start of replacement for this
    466             bsize );                                           // length of replacement for this
    467         append( pasting,
    468             this.Handle.s + this.Handle.lnth,                  // start of SES after this
    469             shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
    470             (this.Handle.s + this.Handle.lnth) );              // length of SES, after this
    471 
    472         // The above string building can trigger compaction.
    473         // The reference points (that are arguments of the string building) may move during that building.
    474         // From this point on, they are stable.
    475 
    476         assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
    477             pasting.Handle.s,
    478             pasting.Handle.lnth,
    479             &pasting.Handle, bsize);
    480     }
    481 
    482     return this;
    483 }
    484 
    485 string_res & assign(string_res &this, const char* buffer, size_t bsize) {
    486     return assign_(this, buffer, bsize, *0p);
    487 }
    488 
    489 string_res & ?=?(string_res &s, char other) {
    490     return assign(s, &other, 1);
     346        MoveThisAfter( p->Handle, pasting.Handle );     // move substring handle to maintain sorted order by string position
     347    }
     348}
     349
     350void ?=?(string_res &s, const char* other) {
     351    assign(s, other, strlen(other));
     352}
     353
     354void ?=?(string_res &s, char other) {
     355    assign(s, &other, 1);
    491356}
    492357
    493358// Copy assignment operator
    494 string_res & ?=?(string_res & this, const string_res & rhs) with( this ) {
    495     return assign_(this, rhs.Handle.s, rhs.Handle.lnth, rhs);
    496 }
    497 
    498 string_res & ?=?(string_res & this, string_res & rhs) with( this ) {
     359void ?=?(string_res & this, const string_res & rhs) with( this ) {
     360    assign(this, rhs.Handle.s, rhs.Handle.lnth);
     361}
     362
     363void ?=?(string_res & this, string_res & rhs) with( this ) {
    499364    const string_res & rhs2 = rhs;
    500     return this = rhs2;
     365    this = rhs2;
    501366}
    502367
     
    509374    s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
    510375    s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
    511     // s.shareEditSet_next = &s;
    512     // s.shareEditSet_prev = &s;
    513 
    514     if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
    515         delete( s.Handle.ulink );
    516     }
     376    s.shareEditSet_next = &s;
     377    s.shareEditSet_prev = &s;
    517378}
    518379
     
    526387}
    527388
    528 void assignAt(const string_res &s, size_t index, char val) {
    529     string_res editZone = { s, SHARE_EDITS, index, index+1 };
    530     assign(editZone, &val, 1);
    531 }
    532 
    533389
    534390///////////////////////////////////////////////////////////////////
     
    536392
    537393void append(string_res &str1, const char * buffer, size_t bsize) {
    538     size_t clnth = str1.Handle.lnth + bsize;
    539     if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?
     394    size_t clnth = size(str1) + bsize;
     395    if ( str1.Handle.s + size(str1) == buffer ) { // already juxtapose ?
    540396        // no-op
    541397    } else {                                            // must copy some text
    542         if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
    543             VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area
     398        if ( str1.Handle.s + size(str1) == VbyteAlloc(HeapArea, 0) ) { // str1 at end of string area ?
     399            VbyteAlloc(HeapArea, bsize); // create room for 2nd part at the end of string area
    544400        } else {                                        // copy the two parts
    545             char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );
    546             char * str1oldBuf = str1.Handle.s;  // must read after VbyteAlloc call in case it gs's
    547             str1.Handle.s = str1newBuf;
    548             memcpy( str1.Handle.s, str1oldBuf,  str1.Handle.lnth );
     401            char * str1oldBuf = str1.Handle.s;
     402            str1.Handle.s = VbyteAlloc( HeapArea, clnth );
     403            ByteCopy( HeapArea, str1.Handle.s, 0, str1.Handle.lnth, str1oldBuf, 0, str1.Handle.lnth);
    549404        } // if
    550         memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );
     405        ByteCopy( HeapArea, str1.Handle.s, str1.Handle.lnth, bsize, (char*)buffer, 0, (int)bsize);
     406        //       VbyteHeap & this, char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth
    551407    } // if
    552408    str1.Handle.lnth = clnth;
     
    561417}
    562418
     419void ?+=?(string_res &s, const char* other) {
     420    append( s, other, strlen(other) );
     421}
    563422
    564423
     
    570429
    571430bool ?==?(const string_res &s1, const string_res &s2) {
    572     return ByteCmp( s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0;
     431    return ByteCmp( HeapArea, s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0;
    573432}
    574433
     
    596455
    597456int find(const string_res &s, char search) {
    598     return findFrom(s, 0, search);
    599 }
    600 
    601 int findFrom(const string_res &s, size_t fromPos, char search) {
    602     // FIXME: This paricular overload (find of single char) is optimized to use memchr.
    603     // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match.
    604     char * searchFrom = s.Handle.s + fromPos;
    605     size_t searchLnth = s.Handle.lnth - fromPos;
    606     int searchVal = search;
    607     char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth);
    608     if (foundAt == 0p) return s.Handle.lnth;
    609     else return foundAt - s.Handle.s;
    610 }
     457    for (i; size(s)) {
     458        if (s[i] == search) return i;
     459    }
     460    return size(s);
     461}
     462
     463    /* Remaining implementations essentially ported from Sunjay's work */
    611464
    612465int find(const string_res &s, const string_res &search) {
    613     return findFrom(s, 0, search);
    614 }
    615 
    616 int findFrom(const string_res &s, size_t fromPos, const string_res &search) {
    617     return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
     466    return find(s, search.Handle.s, search.Handle.lnth);
    618467}
    619468
    620469int find(const string_res &s, const char* search) {
    621     return findFrom(s, 0, search);
    622 }
    623 int findFrom(const string_res &s, size_t fromPos, const char* search) {
    624     return findFrom(s, fromPos, search, strlen(search));
     470    return find(s, search, strlen(search));
    625471}
    626472
    627473int find(const string_res &s, const char* search, size_t searchsize) {
    628     return findFrom(s, 0, search, searchsize);
    629 }
    630 
    631 int findFrom(const string_res &s, size_t fromPos, const char* search, size_t searchsize) {
    632 
    633     /* Remaining implementations essentially ported from Sunjay's work */
    634 
    635 
    636474    // FIXME: This is a naive algorithm. We probably want to switch to someting
    637475    // like Boyer-Moore in the future.
     
    643481    }
    644482
    645     for (size_t i = fromPos; i < s.Handle.lnth; i++) {
     483    for (size_t i = 0; i < s.Handle.lnth; i++) {
    646484        size_t remaining = s.Handle.lnth - i;
    647485        // Never going to find the search string if the remaining string is
     
    758596// Add a new HandleNode node n after the current HandleNode node.
    759597
    760 static void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {
     598static inline void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {
    761599#ifdef VbyteDebug
    762600    serr | "enter:AddThisAfter, this:" | &this | " n:" | &n;
    763601#endif // VbyteDebug
    764     // Performance note: we are on the critical path here. MB has ensured that the verifies don't contribute to runtime (are compiled away, like they're supposed to be).
    765     verify( n.ulink != 0p );
    766     verify( this.ulink == n.ulink );
    767602    flink = n.flink;
    768603    blink = &n;
     
    789624// Delete the current HandleNode node.
    790625
    791 static void DeleteNode( HandleNode & this ) with(this) {
     626static inline void DeleteNode( HandleNode & this ) with(this) {
    792627#ifdef VbyteDebug
    793628    serr | "enter:DeleteNode, this:" | &this;
     
    803638
    804639// Allocates specified storage for a string from byte-string area. If not enough space remains to perform the
    805 // allocation, the garbage collection routine is called.
    806 
    807 static char * VbyteAlloc( VbyteHeap & this, int size ) with(this) {
     640// allocation, the garbage collection routine is called and a second attempt is made to allocate the space. If the
     641// second attempt fails, a further attempt is made to create a new, larger byte-string area.
     642
     643static inline char * VbyteAlloc( VbyteHeap & this, int size ) with(this) {
    808644#ifdef VbyteDebug
    809645    serr | "enter:VbyteAlloc, size:" | size;
     
    814650    NoBytes = ( uintptr_t )EndVbyte + size;
    815651    if ( NoBytes > ( uintptr_t )ExtVbyte ) {            // enough room for new byte-string ?
    816                 garbage( this, size );                                  // firer up the garbage collector
    817                 verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte  && "garbage run did not free up required space" );
     652                garbage( this );                                        // firer up the garbage collector
     653                NoBytes = ( uintptr_t )EndVbyte + size;         // try again
     654                if ( NoBytes > ( uintptr_t )ExtVbyte ) {        // enough room for new byte-string ?
     655assert( 0 && "need to implement actual growth" );
     656                        // extend( size );                              // extend the byte-string area
     657                } // if
    818658    } // if
    819659    r = EndVbyte;
     
    826666
    827667
    828 // Adjusts the last allocation in this heap by delta bytes, or resets this heap to be able to offer
    829 // new allocations of its original size + delta bytes. Positive delta means bigger;
    830 // negative means smaller.  A null return indicates that the original heap location has room for
    831 // the requested growth.  A non-null return indicates that copying to a new location is required
    832 // but has not been done; the returned value is the old heap storage location; `this` heap is
    833 // modified to reference the new location.  In the copy-requred case, the caller should use
    834 // VbyteAlloc to claim the new space, while doing optimal copying from old to new, then free old.
    835 
    836 static char * VbyteTryAdjustLast( VbyteHeap & this, int delta ) with(this) {
    837 
    838     if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {
    839         // room available
    840         EndVbyte += delta;
    841         return 0p;
    842     }
    843 
    844     char *oldBytes = StartVbyte;
    845 
    846     NoOfExtensions += 1;
    847     CurrSize *= 2;
    848     StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    849     ExtVbyte = StartVbyte + CurrSize;
    850 
    851     return oldBytes;
    852 }
    853 
    854 
    855668// Move an existing HandleNode node h somewhere after the current HandleNode node so that it is in ascending order by
    856669// the address in the byte string area.
    857670
    858 static void MoveThisAfter( HandleNode & this, const HandleNode  & h ) with(this) {
     671static inline void MoveThisAfter( HandleNode & this, const HandleNode  & h ) with(this) {
    859672#ifdef VbyteDebug
    860673    serr | "enter:MoveThisAfter, this:" | & this | " h:" | & h;
    861674#endif // VbyteDebug
    862     verify( h.ulink != 0p );
    863     verify( this.ulink == h.ulink );
    864675    if ( s < h.s ) {                                    // check argument values
    865676                // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
    866677                //      | ( h->s ) | " and keep handles in ascending order";
    867678                // exit(-1 );
    868                 verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
     679                assert( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
    869680    } // if
    870681
     
    898709//######################### VbyteHeap #########################
    899710
     711// Move characters from one location in the byte-string area to another. The routine handles the following situations:
     712//
     713// if the |Src| > |Dst| => truncate
     714// if the |Dst| > |Src| => pad Dst with blanks
     715
     716void ByteCopy( VbyteHeap & this, char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth ) {
     717    for ( int i = 0; i < DstLnth; i += 1 ) {
     718      if ( i == SrcLnth ) {                             // |Dst| > |Src|
     719            for ( ; i < DstLnth; i += 1 ) {             // pad Dst with blanks
     720                Dst[DstStart + i] = ' ';
     721            } // for
     722            break;
     723        } // exit
     724        Dst[DstStart + i] = Src[SrcStart + i];
     725    } // for
     726} // ByteCopy
     727
    900728// Compare two byte strings in the byte-string area. The routine returns the following values:
    901729//
     
    904732// -1 => Src1-byte-string < Src2-byte-string
    905733
    906 int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth ) {
     734int ByteCmp( VbyteHeap & this, char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth )  with(this) {
    907735#ifdef VbyteDebug
    908736    serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
     
    961789    h = Header.flink;                                   // ignore header node
    962790    for (;;) {
    963                 memmove( EndVbyte, h->s, h->lnth );
     791                ByteCopy( this, EndVbyte, 0, h->lnth, h->s, 0, h->lnth );
    964792                obase = h->s;
    965793                h->s = EndVbyte;
     
    982810
    983811
    984 static double heap_expansion_freespace_threshold = 0.1;  // default inherited from prior work: expand heap when less than 10% "free" (i.e. garbage)
    985                                                          // probably an unreasonable default, but need to assess early-round tests on changing it
    986 
    987 void TUNING_set_string_heap_liveness_threshold( double val ) {
    988     heap_expansion_freespace_threshold = 1.0 - val;
    989 }
    990 
    991 
    992812// Garbage determines the amount of free space left in the heap and then reduces, leave the same, or extends the size of
    993813// the heap.  The heap is then compacted in the existing heap or into the newly allocated heap.
    994814
    995 void garbage(VbyteHeap & this, int minreq ) with(this) {
     815void garbage(VbyteHeap & this ) with(this) {
    996816#ifdef VbyteDebug
    997817    serr | "enter:garbage";
     
    1017837    AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
    1018838   
    1019     if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) {   // free space less than threshold or not enough to serve cur request
    1020 
    1021                 extend( this, max( CurrSize, minreq ) );                                // extend the heap
     839    if ( AmountFree < ( int )( CurrSize * 0.1 )) {      // free space less than 10% ?
     840
     841assert( 0 && "need to implement actual growth" );
     842//              extend( CurrSize );                             // extend the heap
    1022843
    1023844                        //  Peter says, "This needs work before it should be used."
     
    1025846                        //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
    1026847
    1027         // `extend` implies a `compaction` during the copy
    1028 
    1029     } else {
    1030         compaction(this);                                       // in-place
    1031     }// if
     848    } // if
     849    compaction(this);                                   // compact the byte area, in the same or new heap area
    1032850#ifdef VbyteDebug
    1033851    {
     
    1049867#undef VbyteDebug
    1050868
     869//WIP
     870#if 0
    1051871
    1052872
     
    1054874// area is deleted.
    1055875
    1056 void extend( VbyteHeap & this, int size ) with (this) {
     876void VbyteHeap::extend( int size ) {
    1057877#ifdef VbyteDebug
    1058878    serr | "enter:extend, size:" | size;
     
    1064884   
    1065885    CurrSize += size > InitSize ? size : InitSize;      // minimum extension, initial size
    1066     StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     886    StartVbyte = EndVbyte = new char[CurrSize];
    1067887    ExtVbyte = (void *)( StartVbyte + CurrSize );
    1068     compaction(this);                                   // copy from old heap to new & adjust pointers to new heap
    1069     free( OldStartVbyte );                              // release old heap
     888    compaction();                                       // copy from old heap to new & adjust pointers to new heap
     889    delete OldStartVbyte;                               // release old heap
    1070890#ifdef VbyteDebug
    1071891    serr | "exit:extend, CurrSize:" | CurrSize;
     
    1073893} // extend
    1074894
    1075 //WIP
    1076 #if 0
    1077895
    1078896// Extend the size of the byte-string area by creating a new area and copying the old area into it. The old byte-string
  • libcfa/src/containers/string_res.hfa

    r92538ab r4559b34  
    1717
    1818#include <fstream.hfa>
    19 #include <string.h>    // e.g. strlen
    2019
    2120   
     
    2827    HandleNode *flink;                                  // forward link
    2928    HandleNode *blink;                                  // backward link
    30     VbyteHeap *ulink;                   // upward link
    3129
    3230    char *s;                                            // pointer to byte string
     
    3432}; // HandleNode
    3533
    36 VbyteHeap * DEBUG_string_heap();
    37 size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap );
     34void ?{}( HandleNode & );                       // constructor for header node
     35
     36void ?{}( HandleNode &, VbyteHeap & );          // constructor for nodes in the handle list
     37void ^?{}( HandleNode & );                      // destructor for handle nodes
     38
     39extern VbyteHeap * DEBUG_string_heap;
    3840size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap );
    3941const char * DEBUG_string_heap_start( VbyteHeap * heap );
    4042
    41 void TUNING_set_string_heap_liveness_threshold( double val );
    4243
    4344//######################### String #########################
     
    4647struct string_res {
    4748    HandleNode Handle; // chars, start, end, global neighbours
    48     bool shareEditSet_owns_ulink;
    4949    string_res * shareEditSet_prev;
    5050    string_res * shareEditSet_next;
     
    7474// Constructors, Assignment Operators, Destructor
    7575void ?{}(string_res &s); // empty string
     76void ?{}(string_res &s, const char* initial); // copy from string literal (NULL-terminated)
    7677void ?{}(string_res &s, const char* buffer, size_t bsize); // copy specific length from buffer
    77 static inline void ?{}(string_res &s, const char* rhs) { // copy from string literal (NULL-terminated)
    78     (s){ rhs, strlen(rhs) };
    79 }
    8078
    8179void ?{}(string_res &s, const string_res & s2) = void;
     
    8886}
    8987
    90 string_res & assign(string_res &s, const char* buffer, size_t bsize); // copy specific length from buffer
    91 static inline string_res & ?=?(string_res &s, const char* other) {  // copy from string literal (NULL-terminated)
    92     return assign(s, other, strlen(other));
    93 }
    94 string_res & ?=?(string_res &s, const string_res &other);
    95 string_res & ?=?(string_res &s, string_res &other);
    96 string_res & ?=?(string_res &s, char other);
     88void assign(string_res &s, const char* buffer, size_t bsize); // copy specific length from buffer
     89void ?=?(string_res &s, const char* other); // copy from string literal (NULL-terminated)
     90void ?=?(string_res &s, const string_res &other);
     91void ?=?(string_res &s, string_res &other);
     92void ?=?(string_res &s, char other);
    9793
    9894void ^?{}(string_res &s);
     
    10399
    104100// Concatenation
    105 void append(string_res &s, const char* buffer, size_t bsize);
    106101void ?+=?(string_res &s, char other); // append a character
    107102void ?+=?(string_res &s, const string_res &s2); // append-concatenate to first string
    108 static inline void ?+=?(string_res &s, const char* other) {
    109     append( s, other, strlen(other) );
    110 }
     103void ?+=?(string_res &s, const char* other);
     104void append(string_res &s, const char* buffer, size_t bsize);
    111105
    112106// Character access
    113 void assignAt(const string_res &s, size_t index, char val);
    114107char ?[?](const string_res &s, size_t index); // Mike changed to ret by val from Sunjay's ref, to match Peter's
    115108//char codePointAt(const string_res &s, size_t index); // revisit under Unicode
     
    128121int find(const string_res &s, const char* search);
    129122int find(const string_res &s, const char* search, size_t searchsize);
    130 
    131 int findFrom(const string_res &s, size_t fromPos, char search);
    132 int findFrom(const string_res &s, size_t fromPos, const string_res &search);
    133 int findFrom(const string_res &s, size_t fromPos, const char* search);
    134 int findFrom(const string_res &s, size_t fromPos, const char* search, size_t searchsize);
    135123
    136124bool includes(const string_res &s, const string_res &search);
  • libcfa/src/device/cpu.hfa

    r92538ab r4559b34  
    1313// Update Count     :
    1414//
    15 
    16 #pragma once
    1715
    1816#include <stddef.h>
  • libcfa/src/fstream.cfa

    r92538ab r4559b34  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Apr  9 14:55:54 2022
    13 // Update Count     : 515
     12// Last Modified On : Mon Jan 10 08:45:05 2022
     13// Update Count     : 513
    1414//
    1515
     
    161161    for ( cnt; 10 ) {
    162162                errno = 0;
    163                 disable_interrupts();
    164163                len = vfprintf( (FILE *)(os.file$), format, args );
    165                 enable_interrupts();
    166164          if ( len != EOF || errno != EINTR ) break;            // timer interrupt ?
    167165          if ( cnt == 9 ) abort( "ofstream fmt EINTR spinning exceeded" );
     
    295293    for () {                                                                                    // no check for EINTR limit waiting for keyboard input
    296294                errno = 0;
    297                 disable_interrupts();
    298295                len = vfscanf( (FILE *)(is.file$), format, args );
    299                 enable_interrupts();
    300296          if ( len != EOF || errno != EINTR ) break;            // timer interrupt ?
    301297    } // for
  • libcfa/src/math.trait.hfa

    r92538ab r4559b34  
    1616#pragma once
    1717
    18 trait Not( U ) {
    19         void ?{}( U &, zero_t );
    20         int !?( U );
     18trait Not( T ) {
     19        void ?{}( T &, zero_t );
     20        int !?( T );
    2121}; // Not
    2222
     
    2626}; // Equality
    2727
    28 trait Relational( U | Equality( U ) ) {
    29         int ?<?( U, U );
    30         int ?<=?( U, U );
    31         int ?>?( U, U );
    32         int ?>=?( U, U );
     28trait Relational( T | Equality( T ) ) {
     29        int ?<?( T, T );
     30        int ?<=?( T, T );
     31        int ?>?( T, T );
     32        int ?>=?( T, T );
    3333}; // Relational
    3434
     
    3939}; // Signed
    4040
    41 trait Additive( U | Signed( U ) ) {
    42         U ?+?( U, U );
    43         U ?-?( U, U );
    44         U ?+=?( U &, U );
    45         U ?-=?( U &, U );
     41trait Additive( T | Signed( T ) ) {
     42        T ?+?( T, T );
     43        T ?-?( T, T );
     44        T ?+=?( T &, T );
     45        T ?-=?( T &, T );
    4646}; // Additive
    4747
     
    4949        void ?{}( T &, one_t );
    5050        // T ?++( T & );
    51         // T ++?( T & );
     51        // T ++?( T &);
    5252        // T ?--( T & );
    5353        // T --?( T & );
    5454}; // Incdec
    5555
    56 trait Multiplicative( U | Incdec( U ) ) {
    57         U ?*?( U, U );
    58         U ?/?( U, U );
    59         U ?%?( U, U );
    60         U ?/=?( U &, U );
     56trait Multiplicative( T | Incdec( T ) ) {
     57        T ?*?( T, T );
     58        T ?/?( T, T );
     59        T ?%?( T, T );
     60        T ?/=?( T &, T );
    6161}; // Multiplicative
    6262
  • libcfa/src/stdlib.hfa

    r92538ab r4559b34  
    1010// Created On       : Thu Jan 28 17:12:35 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Feb 12 17:22:25 2022
    13 // Update Count     : 643
     12// Last Modified On : Thu Feb 10 18:34:58 2022
     13// Update Count     : 641
    1414//
    1515
     
    412412void set_seed( PRNG & prng, uint32_t seed_ );
    413413static inline {
    414         void ?{}( PRNG & prng ) with( prng ) { callcnt = 0; set_seed( prng, rdtscl() ); } // random seed
    415         void ?{}( PRNG & prng, uint32_t seed ) with( prng ) { callcnt = 0; set_seed( prng, seed ); } // fixed seed
     414        void ?{}( PRNG & prng ) { set_seed( prng, rdtscl() ); } // random seed
     415        void ?{}( PRNG & prng, uint32_t seed ) { set_seed( prng, seed ); } // fixed seed
    416416        uint32_t get_seed( PRNG & prng ) __attribute__(( warn_unused_result )) with( prng ) { return seed; } // get seed
    417417        uint32_t prng( PRNG & prng ) __attribute__(( warn_unused_result )) with( prng ) { callcnt += 1; return LCG( state ); } // [0,UINT_MAX]
  • src/AST/Convert.cpp

    r92538ab r4559b34  
    99// Author           : Thierry Delisle
    1010// Created On       : Thu May 09 15::37::05 2019
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar 16 15:01:00 2022
    13 // Update Count     : 42
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Wed Feb  2 13:19:22 2022
     13// Update Count     : 41
    1414//
    1515
     
    4949//================================================================================================
    5050namespace ast {
    51 // These are the shared local information used by ConverterNewToOld and
    52 // ConverterOldToNew to update the global information in the two versions.
    53 
    54 static ast::ptr<ast::Type> sizeType = nullptr;
    55 static const ast::FunctionDecl * dereferenceOperator = nullptr;
    56 static const ast::StructDecl   * dtorStruct = nullptr;
    57 static const ast::FunctionDecl * dtorStructDestroy = nullptr;
     51
     52// This is to preserve the FindSpecialDecls hack. It does not (and perhaps should not)
     53// allow us to use the same stratagy in the new ast.
     54// xxx - since convert back pass works, this concern seems to be unnecessary.
     55
     56// these need to be accessed in new FixInit now
     57ast::ptr<ast::Type> sizeType = nullptr;
     58const ast::FunctionDecl * dereferenceOperator = nullptr;
     59const ast::StructDecl   * dtorStruct = nullptr;
     60const ast::FunctionDecl * dtorStructDestroy = nullptr;
    5861
    5962}
     
    354357        }
    355358
    356         void clausePostamble( Statement * stmt, const ast::StmtClause * node ) {
    357                 stmt->location = node->location;
    358                 this->node = stmt;
    359         }
    360 
    361359        const ast::CompoundStmt * visit( const ast::CompoundStmt * node ) override final {
    362360                if ( inCache( node ) ) return nullptr;
     
    407405                auto stmt = new SwitchStmt(
    408406                        get<Expression>().accept1( node->cond ),
    409                         get<Statement>().acceptL( node->cases )
     407                        get<Statement>().acceptL( node->stmts )
    410408                );
    411409                return stmtPostamble( stmt, node );
    412410        }
    413411
    414         const ast::CaseClause * visit( const ast::CaseClause * node ) override final {
     412        const ast::Stmt * visit( const ast::CaseStmt * node ) override final {
    415413                if ( inCache( node ) ) return nullptr;
    416414                auto stmt = new CaseStmt(
     
    419417                        node->isDefault()
    420418                );
    421                 clausePostamble( stmt, node );
    422                 return nullptr;
     419                return stmtPostamble( stmt, node );
    423420        }
    424421
     
    516513        }
    517514
    518         const ast::CatchClause * visit( const ast::CatchClause * node ) override final {
     515        const ast::Stmt * visit( const ast::CatchStmt * node ) override final {
    519516                if ( inCache( node ) ) return nullptr;
    520517                CatchStmt::Kind kind;
     
    527524                        break;
    528525                default:
    529                         assertf(false, "Invalid ast::ExceptionKind: %d\n", node->kind);
     526                        assertf(false, "Invalid ast::CatchStmt::Kind: %d\n", node->kind);
    530527                }
    531528                auto stmt = new CatchStmt(
     
    535532                        get<Statement>().accept1( node->body )
    536533                );
    537                 return clausePostamble( stmt, node ), nullptr;
    538         }
    539 
    540         const ast::FinallyClause * visit( const ast::FinallyClause * node ) override final {
     534                return stmtPostamble( stmt, node );
     535        }
     536
     537        const ast::Stmt * visit( const ast::FinallyStmt * node ) override final {
    541538                if ( inCache( node ) ) return nullptr;
    542539                auto stmt = new FinallyStmt( get<CompoundStmt>().accept1( node->body ) );
    543                 return clausePostamble( stmt, node ), nullptr;
     540                return stmtPostamble( stmt, node );
    544541        }
    545542
     
    18971894                        old->location,
    18981895                        GET_ACCEPT_1(condition, Expr),
    1899                         GET_ACCEPT_V(statements, CaseClause),
     1896                        GET_ACCEPT_V(statements, Stmt),
    19001897                        GET_LABELS_V(old->labels)
    19011898                );
     
    19051902        virtual void visit( const CaseStmt * old ) override final {
    19061903                if ( inCache( old ) ) return;
    1907                 this->node = new ast::CaseClause(
     1904                this->node = new ast::CaseStmt(
    19081905                        old->location,
    19091906                        GET_ACCEPT_1(condition, Expr),
    1910                         GET_ACCEPT_V(stmts, Stmt)
    1911                 );
    1912                 auto labels = GET_LABELS_V(old->labels);
    1913                 assertf(labels.empty(), "Labels found on CaseStmt.");
     1907                        GET_ACCEPT_V(stmts, Stmt),
     1908                        GET_LABELS_V(old->labels)
     1909                );
    19141910                cache.emplace( old, this->node );
    19151911        }
     
    20192015                        old->location,
    20202016                        GET_ACCEPT_1(block, CompoundStmt),
    2021                         GET_ACCEPT_V(handlers, CatchClause),
    2022                         GET_ACCEPT_1(finallyBlock, FinallyClause),
     2017                        GET_ACCEPT_V(handlers, CatchStmt),
     2018                        GET_ACCEPT_1(finallyBlock, FinallyStmt),
    20232019                        GET_LABELS_V(old->labels)
    20242020                );
     
    20402036                }
    20412037
    2042                 this->node = new ast::CatchClause(
     2038                this->node = new ast::CatchStmt(
    20432039                        old->location,
    20442040                        kind,
    20452041                        GET_ACCEPT_1(decl, Decl),
    20462042                        GET_ACCEPT_1(cond, Expr),
    2047                         GET_ACCEPT_1(body, Stmt)
    2048                 );
    2049                 auto labels = GET_LABELS_V(old->labels);
    2050                 assertf(labels.empty(), "Labels found on CatchStmt.");
     2043                        GET_ACCEPT_1(body, Stmt),
     2044                        GET_LABELS_V(old->labels)
     2045                );
    20512046                cache.emplace( old, this->node );
    20522047        }
     
    20542049        virtual void visit( const FinallyStmt * old ) override final {
    20552050                if ( inCache( old ) ) return;
    2056                 this->node = new ast::FinallyClause(
    2057                         old->location,
    2058                         GET_ACCEPT_1(block, CompoundStmt)
    2059                 );
    2060                 auto labels = GET_LABELS_V(old->labels);
    2061                 assertf(labels.empty(), "Labels found on FinallyStmt.");
     2051                this->node = new ast::FinallyStmt(
     2052                        old->location,
     2053                        GET_ACCEPT_1(block, CompoundStmt),
     2054                        GET_LABELS_V(old->labels)
     2055                );
    20622056                cache.emplace( old, this->node );
    20632057        }
     
    27242718
    27252719                for (auto & param : foralls) {
    2726                         ty->forall.emplace_back(new ast::TypeInstType(param));
     2720                        ty->forall.emplace_back(new ast::TypeInstType(param->name, param));
    27272721                        for (auto asst : param->assertions) {
    27282722                                ty->assertions.emplace_back(new ast::VariableExpr({}, asst));
  • src/AST/Decl.cpp

    r92538ab r4559b34  
    3939        if ( uniqueId ) return;  // ensure only set once
    4040        uniqueId = ++lastUniqueId;
    41         // The extra readonly pointer is causing some reference counting issues.
    42         // idMap[ uniqueId ] = this;
     41        idMap[ uniqueId ] = this;
    4342}
    4443
    4544readonly<Decl> Decl::fromId( UniqueId id ) {
    46         // Right now this map is always empty, so don't use it.
    47         assert( false );
    4845        IdMapType::const_iterator i = idMap.find( id );
    4946        if ( i != idMap.end() ) return i->second;
     
    6865        }
    6966        for (auto & tp : this->type_params) {
    70                 ftype->forall.emplace_back(new TypeInstType(tp));
     67                ftype->forall.emplace_back(new TypeInstType(tp->name, tp));
    7168                for (auto & ap: tp->assertions) {
    7269                        ftype->assertions.emplace_back(new VariableExpr(loc, ap));
  • src/AST/Fwd.hpp

    r92538ab r4559b34  
    4747class ForStmt;
    4848class SwitchStmt;
    49 class CaseClause;
     49class CaseStmt;
    5050class BranchStmt;
    5151class ReturnStmt;
    5252class ThrowStmt;
    5353class TryStmt;
    54 class CatchClause;
    55 class FinallyClause;
     54class CatchStmt;
     55class FinallyStmt;
    5656class SuspendStmt;
    5757class WaitForStmt;
     
    141141
    142142class TranslationUnit;
    143 class TranslationGlobal;
     143// TODO: Get from the TranslationUnit:
     144extern ptr<Type> sizeType;
     145extern const FunctionDecl * dereferenceOperator;
     146extern const StructDecl   * dtorStruct;
     147extern const FunctionDecl * dtorStructDestroy;
    144148
    145149}
  • src/AST/GenericSubstitution.cpp

    r92538ab r4559b34  
    4545                        visit_children = false;
    4646                        const AggregateDecl * aggr = ty->aggr();
    47                         sub = TypeSubstitution( aggr->params, ty->params );
     47                        sub = TypeSubstitution{ aggr->params.begin(), aggr->params.end(), ty->params.begin() };
    4848                }
    4949
  • src/AST/Node.cpp

    r92538ab r4559b34  
    99// Author           : Thierry Delisle
    1010// Created On       : Thu May 16 14:16:00 2019
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Mar 25 10:30:00 2022
    13 // Update Count     : 4
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Tue Feb  1 09:09:39 2022
     13// Update Count     : 3
    1414//
    1515
     
    1919#include <csignal>  // MEMORY DEBUG -- for raise
    2020#include <iostream>
    21 #include <utility>
    2221
    2322#include "Attribute.hpp"
     
    7776void ast::ptr_base<node_t, ref_t>::_check() const {
    7877        // if(node) assert(node->was_ever_strong == false || node->strong_count > 0);
    79 }
    80 
    81 template< typename node_t, enum ast::Node::ref_type ref_t >
    82 void ast::ptr_base<node_t, ref_t>::swap( ptr_base & other ) noexcept {
    83         std::swap( this->node, other.node );
    84         _trap( this->node );
    85         _trap( other.node );
    8678}
    8779
     
    160152template class ast::ptr_base< ast::SwitchStmt, ast::Node::ref_type::weak >;
    161153template class ast::ptr_base< ast::SwitchStmt, ast::Node::ref_type::strong >;
    162 template class ast::ptr_base< ast::CaseClause, ast::Node::ref_type::weak >;
    163 template class ast::ptr_base< ast::CaseClause, ast::Node::ref_type::strong >;
     154template class ast::ptr_base< ast::CaseStmt, ast::Node::ref_type::weak >;
     155template class ast::ptr_base< ast::CaseStmt, ast::Node::ref_type::strong >;
    164156template class ast::ptr_base< ast::BranchStmt, ast::Node::ref_type::weak >;
    165157template class ast::ptr_base< ast::BranchStmt, ast::Node::ref_type::strong >;
     
    170162template class ast::ptr_base< ast::TryStmt, ast::Node::ref_type::weak >;
    171163template class ast::ptr_base< ast::TryStmt, ast::Node::ref_type::strong >;
    172 template class ast::ptr_base< ast::CatchClause, ast::Node::ref_type::weak >;
    173 template class ast::ptr_base< ast::CatchClause, ast::Node::ref_type::strong >;
    174 template class ast::ptr_base< ast::FinallyClause, ast::Node::ref_type::weak >;
    175 template class ast::ptr_base< ast::FinallyClause, ast::Node::ref_type::strong >;
     164template class ast::ptr_base< ast::CatchStmt, ast::Node::ref_type::weak >;
     165template class ast::ptr_base< ast::CatchStmt, ast::Node::ref_type::strong >;
     166template class ast::ptr_base< ast::FinallyStmt, ast::Node::ref_type::weak >;
     167template class ast::ptr_base< ast::FinallyStmt, ast::Node::ref_type::strong >;
    176168template class ast::ptr_base< ast::WaitForStmt, ast::Node::ref_type::weak >;
    177169template class ast::ptr_base< ast::WaitForStmt, ast::Node::ref_type::strong >;
  • src/AST/Node.hpp

    r92538ab r4559b34  
    1010// Created On       : Wed May 8 10:27:04 2019
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Mar 25 10:33:00 2022
    13 // Update Count     : 7
     12// Last Modified On : Fri Jun 5 9:47:00 2020
     13// Update Count     : 6
    1414//
    1515
     
    103103
    104104/// Mutate a node field (only clones if not equal to existing value)
    105 template<typename node_t, typename super_t, typename field_t, typename assn_t>
    106 const node_t * mutate_field( const node_t * node, field_t super_t::* field, assn_t && val ) {
     105template<typename node_t, typename parent_t, typename field_t, typename assn_t>
     106const node_t * mutate_field( const node_t * node, field_t parent_t::* field, assn_t && val ) {
    107107        // skip mutate if equivalent
    108108        if ( node->*field == val ) return node;
     
    115115
    116116/// Mutate a single index of a node field (only clones if not equal to existing value)
    117 template<typename node_t, typename super_t, typename coll_t, typename ind_t, typename field_t>
     117template<typename node_t, typename parent_t, typename coll_t, typename ind_t, typename field_t>
    118118const node_t * mutate_field_index(
    119         const node_t * node, coll_t super_t::* field, ind_t i, field_t && val
     119        const node_t * node, coll_t parent_t::* field, ind_t i, field_t && val
    120120) {
    121121        // skip mutate if equivalent
     
    129129
    130130/// Mutate an entire indexed collection by cloning to accepted value
    131 template<typename node_t, typename super_t, typename coll_t>
    132 const node_t * mutate_each( const node_t * node, coll_t super_t::* field, Visitor & v ) {
     131template<typename node_t, typename parent_t, typename coll_t>
     132const node_t * mutate_each( const node_t * node, coll_t parent_t::* field, Visitor & v ) {
    133133        for ( unsigned i = 0; i < (node->*field).size(); ++i ) {
    134134                node = mutate_field_index( node, field, i, (node->*field)[i]->accept( v ) );
     
    230230        }
    231231
    232         /// Swaps the nodes contained within two pointers.
    233         void swap( ptr_base & other ) noexcept;
    234 
    235232        const node_t * get() const { _check(); return  node; }
    236233        const node_t * operator->() const { _check(); return  node; }
     
    295292template< typename node_t >
    296293using readonly = ptr_base< node_t, Node::ref_type::weak >;
    297 
    298 /// Non-member swap that an participate in overload resolution.
    299 template< typename node_t, enum Node::ref_type ref_t >
    300 void swap( ptr_base< node_t, ref_t > & l, ptr_base< node_t, ref_t > & r ) {
    301         l.swap( r );
    302 }
    303 
    304294}
    305295
  • src/AST/Pass.hpp

    r92538ab r4559b34  
    149149        const ast::Stmt *             visit( const ast::ForStmt              * ) override final;
    150150        const ast::Stmt *             visit( const ast::SwitchStmt           * ) override final;
    151         const ast::CaseClause *       visit( const ast::CaseClause           * ) override final;
     151        const ast::Stmt *             visit( const ast::CaseStmt             * ) override final;
    152152        const ast::Stmt *             visit( const ast::BranchStmt           * ) override final;
    153153        const ast::Stmt *             visit( const ast::ReturnStmt           * ) override final;
    154154        const ast::Stmt *             visit( const ast::ThrowStmt            * ) override final;
    155155        const ast::Stmt *             visit( const ast::TryStmt              * ) override final;
    156         const ast::CatchClause *      visit( const ast::CatchClause          * ) override final;
    157         const ast::FinallyClause *    visit( const ast::FinallyClause        * ) override final;
     156        const ast::Stmt *             visit( const ast::CatchStmt            * ) override final;
     157        const ast::Stmt *             visit( const ast::FinallyStmt          * ) override final;
    158158        const ast::Stmt *             visit( const ast::SuspendStmt          * ) override final;
    159159        const ast::Stmt *             visit( const ast::WaitForStmt          * ) override final;
  • src/AST/Pass.impl.hpp

    r92538ab r4559b34  
    354354                        // Take all the elements that are different in 'values'
    355355                        // and swap them into 'container'
    356                         if( values[i] != nullptr ) swap(container[i], values[i]);
     356                        if( values[i] != nullptr ) std::swap(container[i], values[i]);
    357357                }
    358358
     
    399399
    400400        template< typename core_t >
    401         template<typename node_t, typename super_t, typename field_t>
     401        template<typename node_t, typename parent_t, typename child_t>
    402402        void ast::Pass< core_t >::maybe_accept(
    403403                const node_t * & parent,
    404                 field_t super_t::*field
     404                child_t parent_t::*child
    405405        ) {
    406                 static_assert( std::is_base_of<super_t, node_t>::value, "Error deducing member object" );
    407 
    408                 if(__pass::skip(parent->*field)) return;
    409                 const auto & old_val = __pass::get(parent->*field, 0);
     406                static_assert( std::is_base_of<parent_t, node_t>::value, "Error deducing member object" );
     407
     408                if(__pass::skip(parent->*child)) return;
     409                const auto & old_val = __pass::get(parent->*child, 0);
    410410
    411411                static_assert( !std::is_same<const ast::Node * &, decltype(old_val)>::value, "ERROR");
     
    417417                if( new_val.differs ) {
    418418                        auto new_parent = __pass::mutate<core_t>(parent);
    419                         new_val.apply(new_parent, field);
     419                        new_val.apply(new_parent, child);
    420420                        parent = new_parent;
    421421                }
     
    423423
    424424        template< typename core_t >
    425         template<typename node_t, typename super_t, typename field_t>
     425        template<typename node_t, typename parent_t, typename child_t>
    426426        void ast::Pass< core_t >::maybe_accept_as_compound(
    427427                const node_t * & parent,
    428                 field_t super_t::*child
     428                child_t parent_t::*child
    429429        ) {
    430                 static_assert( std::is_base_of<super_t, node_t>::value, "Error deducing member object" );
     430                static_assert( std::is_base_of<parent_t, node_t>::value, "Error deducing member object" );
    431431
    432432                if(__pass::skip(parent->*child)) return;
     
    893893        if ( __visit_children() ) {
    894894                maybe_accept( node, &SwitchStmt::cond  );
    895                 maybe_accept( node, &SwitchStmt::cases );
     895                maybe_accept( node, &SwitchStmt::stmts );
    896896        }
    897897
     
    900900
    901901//--------------------------------------------------------------------------
    902 // CaseClause
    903 template< typename core_t >
    904 const ast::CaseClause * ast::Pass< core_t >::visit( const ast::CaseClause * node ) {
    905         VISIT_START( node );
    906 
    907         if ( __visit_children() ) {
    908                 maybe_accept( node, &CaseClause::cond  );
    909                 maybe_accept( node, &CaseClause::stmts );
    910         }
    911 
    912         VISIT_END( CaseClause, node );
     902// CaseStmt
     903template< typename core_t >
     904const ast::Stmt * ast::Pass< core_t >::visit( const ast::CaseStmt * node ) {
     905        VISIT_START( node );
     906
     907        if ( __visit_children() ) {
     908                maybe_accept( node, &CaseStmt::cond  );
     909                maybe_accept( node, &CaseStmt::stmts );
     910        }
     911
     912        VISIT_END( Stmt, node );
    913913}
    914914
     
    964964
    965965//--------------------------------------------------------------------------
    966 // CatchClause
    967 template< typename core_t >
    968 const ast::CatchClause * ast::Pass< core_t >::visit( const ast::CatchClause * node ) {
     966// CatchStmt
     967template< typename core_t >
     968const ast::Stmt * ast::Pass< core_t >::visit( const ast::CatchStmt * node ) {
    969969        VISIT_START( node );
    970970
     
    972972                // catch statements introduce a level of scope (for the caught exception)
    973973                guard_symtab guard { *this };
    974                 maybe_accept( node, &CatchClause::decl );
    975                 maybe_accept( node, &CatchClause::cond );
    976                 maybe_accept_as_compound( node, &CatchClause::body );
    977         }
    978 
    979         VISIT_END( CatchClause, node );
    980 }
    981 
    982 //--------------------------------------------------------------------------
    983 // FinallyClause
    984 template< typename core_t >
    985 const ast::FinallyClause * ast::Pass< core_t >::visit( const ast::FinallyClause * node ) {
    986         VISIT_START( node );
    987 
    988         if ( __visit_children() ) {
    989                 maybe_accept( node, &FinallyClause::body );
    990         }
    991 
    992         VISIT_END( FinallyClause, node );
     974                maybe_accept( node, &CatchStmt::decl );
     975                maybe_accept( node, &CatchStmt::cond );
     976                maybe_accept_as_compound( node, &CatchStmt::body );
     977        }
     978
     979        VISIT_END( Stmt, node );
     980}
     981
     982//--------------------------------------------------------------------------
     983// FinallyStmt
     984template< typename core_t >
     985const ast::Stmt * ast::Pass< core_t >::visit( const ast::FinallyStmt * node ) {
     986        VISIT_START( node );
     987
     988        if ( __visit_children() ) {
     989                maybe_accept( node, &FinallyStmt::body );
     990        }
     991
     992        VISIT_END( Stmt, node );
    993993}
    994994
     
    10541054                        auto n = __pass::mutate<core_t>(node);
    10551055                        for(size_t i = 0; i < new_clauses.size(); i++) {
    1056                                 if(new_clauses.at(i).target.func != nullptr) swap(n->clauses.at(i).target.func, new_clauses.at(i).target.func);
     1056                                if(new_clauses.at(i).target.func != nullptr) std::swap(n->clauses.at(i).target.func, new_clauses.at(i).target.func);
    10571057
    10581058                                for(size_t j = 0; j < new_clauses.at(i).target.args.size(); j++) {
    1059                                         if(new_clauses.at(i).target.args.at(j) != nullptr) swap(n->clauses.at(i).target.args.at(j), new_clauses.at(i).target.args.at(j));
     1059                                        if(new_clauses.at(i).target.args.at(j) != nullptr) std::swap(n->clauses.at(i).target.args.at(j), new_clauses.at(i).target.args.at(j));
    10601060                                }
    10611061
    1062                                 if(new_clauses.at(i).stmt != nullptr) swap(n->clauses.at(i).stmt, new_clauses.at(i).stmt);
    1063                                 if(new_clauses.at(i).cond != nullptr) swap(n->clauses.at(i).cond, new_clauses.at(i).cond);
     1062                                if(new_clauses.at(i).stmt != nullptr) std::swap(n->clauses.at(i).stmt, new_clauses.at(i).stmt);
     1063                                if(new_clauses.at(i).cond != nullptr) std::swap(n->clauses.at(i).cond, new_clauses.at(i).cond);
    10641064                        }
    10651065                        node = n;
     
    21512151
    21522152        if ( __visit_children() ) {
    2153                 bool mutated = false;
    2154                 std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map;
    2155                 for ( const auto & p : node->typeEnv ) {
    2156                         guard_symtab guard { *this };
    2157                         auto new_node = p.second->accept( *this );
    2158                         if (new_node != p.second) mutated = true;
    2159                         new_map.insert({ p.first, new_node });
    2160                 }
    2161                 if (mutated) {
    2162                         auto new_node = __pass::mutate<core_t>( node );
    2163                         new_node->typeEnv.swap( new_map );
    2164                         node = new_node;
     2153                {
     2154                        bool mutated = false;
     2155                        std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map;
     2156                        for ( const auto & p : node->typeEnv ) {
     2157                                guard_symtab guard { *this };
     2158                                auto new_node = p.second->accept( *this );
     2159                                if (new_node != p.second) mutated = true;
     2160                                new_map.insert({ p.first, new_node });
     2161                        }
     2162                        if (mutated) {
     2163                                auto new_node = __pass::mutate<core_t>( node );
     2164                                new_node->typeEnv.swap( new_map );
     2165                                node = new_node;
     2166                        }
    21652167                }
    21662168        }
  • src/AST/Print.cpp

    r92538ab r4559b34  
    597597
    598598                ++indent;
    599                 for ( const ast::CaseClause * stmt : node->cases ) {
     599                for ( const ast::Stmt * stmt : node->stmts ) {
    600600                        stmt->accept( *this );
    601601                }
     
    605605        }
    606606
    607         virtual const ast::CaseClause * visit( const ast::CaseClause * node ) override final {
     607        virtual const ast::Stmt * visit( const ast::CaseStmt * node ) override final {
    608608                if ( node->isDefault() ) {
    609609                        os << indent << "Default ";
     
    687687
    688688                os << indent-1 << "... and handlers:" << endl;
    689                 for ( const ast::CatchClause * stmt : node->handlers ) {
     689                for ( const ast::CatchStmt * stmt : node->handlers ) {
    690690                        os << indent;
    691691                        stmt->accept( *this );
     
    701701        }
    702702
    703         virtual const ast::CatchClause * visit( const ast::CatchClause * node ) override final {
     703        virtual const ast::Stmt * visit( const ast::CatchStmt * node ) override final {
    704704                os << "Catch ";
    705705                switch ( node->kind ) {
     
    726726        }
    727727
    728         virtual const ast::FinallyClause * visit( const ast::FinallyClause * node ) override final {
     728        virtual const ast::Stmt * visit( const ast::FinallyStmt * node ) override final {
    729729                os << "Finally Statement" << endl;
    730730                os << indent << "... with block:" << endl;
  • src/AST/Stmt.hpp

    r92538ab r4559b34  
    99// Author           : Aaron B. Moss
    1010// Created On       : Wed May  8 13:00:00 2019
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Mar 28  9:50:00 2022
    13 // Update Count     : 35
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Wed Feb  2 20:06:41 2022
     13// Update Count     : 34
    1414//
    1515
     
    4747  private:
    4848        Stmt * clone() const override = 0;
    49         MUTATE_FRIEND
    50 };
    51 
    52 // Base statement component node (only serves to group them).
    53 class StmtClause : public ParseNode {
    54   public:
    55         // This is for non-statements that still belong with the statements,
    56         // but are not statements, usually some sort of clause. Often these can
    57         // (and should) be folded into the approprate parent node, but if they
    58         // cannot be, they are sub-types of this type, for organization.
    59 
    60     StmtClause( const CodeLocation & loc )
    61                 : ParseNode(loc) {}
    62 
    63   private:
    64         StmtClause * clone() const override = 0;
    6549        MUTATE_FRIEND
    6650};
     
    174158  public:
    175159        ptr<Expr> cond;
    176         std::vector<ptr<CaseClause>> cases;
    177 
    178         SwitchStmt( const CodeLocation & loc, const Expr * cond,
    179                                 const std::vector<ptr<CaseClause>> && cases,
     160        std::vector<ptr<Stmt>> stmts;
     161
     162        SwitchStmt( const CodeLocation & loc, const Expr * cond, const std::vector<ptr<Stmt>> && stmts,
    180163                                const std::vector<Label> && labels = {} )
    181                 : Stmt(loc, std::move(labels)), cond(cond), cases(std::move(cases)) {}
     164                : Stmt(loc, std::move(labels)), cond(cond), stmts(std::move(stmts)) {}
    182165
    183166        const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
     
    188171
    189172// Case label: case ...: or default:
    190 class CaseClause final : public StmtClause {
     173class CaseStmt final : public Stmt {
    191174  public:
    192175        // Null for the default label.
     
    194177        std::vector<ptr<Stmt>> stmts;
    195178
    196         CaseClause( const CodeLocation & loc, const Expr * cond, const std::vector<ptr<Stmt>> && stmts )
    197                 : StmtClause(loc), cond(cond), stmts(std::move(stmts)) {}
     179        CaseStmt( const CodeLocation & loc, const Expr * cond, const std::vector<ptr<Stmt>> && stmts,
     180                          const std::vector<Label> && labels = {} )
     181                : Stmt(loc, std::move(labels)), cond(cond), stmts(std::move(stmts)) {}
    198182
    199183        bool isDefault() const { return !cond; }
    200184
    201         const CaseClause * accept( Visitor & v ) const override { return v.visit( this ); }
    202   private:
    203         CaseClause * clone() const override { return new CaseClause{ *this }; }
     185        const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
     186  private:
     187        CaseStmt * clone() const override { return new CaseStmt{ *this }; }
    204188        MUTATE_FRIEND
    205189};
     
    314298  public:
    315299        ptr<CompoundStmt> body;
    316         std::vector<ptr<CatchClause>> handlers;
    317         ptr<FinallyClause> finally;
     300        std::vector<ptr<CatchStmt>> handlers;
     301        ptr<FinallyStmt> finally;
    318302
    319303        TryStmt( const CodeLocation & loc, const CompoundStmt * body,
    320                          const std::vector<ptr<CatchClause>> && handlers, const FinallyClause * finally,
     304                         const std::vector<ptr<CatchStmt>> && handlers, const FinallyStmt * finally,
    321305                         const std::vector<Label> && labels = {} )
    322306                : Stmt(loc, std::move(labels)), body(body), handlers(std::move(handlers)), finally(finally) {}
     
    329313
    330314// Catch clause of try statement
    331 class CatchClause final : public StmtClause {
     315class CatchStmt final : public Stmt {
    332316  public:
    333317        ptr<Decl> decl;
     
    336320        ExceptionKind kind;
    337321
    338         CatchClause( const CodeLocation & loc, ExceptionKind kind, const Decl * decl, const Expr * cond,
    339                            const Stmt * body )
    340                 : StmtClause(loc), decl(decl), cond(cond), body(body), kind(kind) {}
    341 
    342         const CatchClause * accept( Visitor & v ) const override { return v.visit( this ); }
    343   private:
    344         CatchClause * clone() const override { return new CatchClause{ *this }; }
     322        CatchStmt( const CodeLocation & loc, ExceptionKind kind, const Decl * decl, const Expr * cond,
     323                           const Stmt * body, const std::vector<Label> && labels = {} )
     324                : Stmt(loc, std::move(labels)), decl(decl), cond(cond), body(body), kind(kind) {}
     325
     326        const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
     327  private:
     328        CatchStmt * clone() const override { return new CatchStmt{ *this }; }
    345329        MUTATE_FRIEND
    346330};
    347331
    348332// Finally clause of try statement
    349 class FinallyClause final : public StmtClause {
     333class FinallyStmt final : public Stmt {
    350334  public:
    351335        ptr<CompoundStmt> body;
    352336
    353         FinallyClause( const CodeLocation & loc, const CompoundStmt * body )
    354                 : StmtClause(loc), body(body) {}
    355 
    356         const FinallyClause * accept( Visitor & v ) const override { return v.visit( this ); }
    357   private:
    358         FinallyClause * clone() const override { return new FinallyClause{ *this }; }
     337        FinallyStmt( const CodeLocation & loc, const CompoundStmt * body,
     338                                 std::vector<Label> && labels = {} )
     339                : Stmt(loc, std::move(labels)), body(body) {}
     340
     341        const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
     342  private:
     343        FinallyStmt * clone() const override { return new FinallyStmt{ *this }; }
    359344        MUTATE_FRIEND
    360345};
  • src/AST/TranslationUnit.hpp

    r92538ab r4559b34  
    1010// Created On       : Tue Jun 11 15:30:00 2019
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Tue Mar 11 11:19:00 2022
    13 // Update Count     : 1
     12// Last Modified On : Tue Jun 11 15:42:00 2019
     13// Update Count     : 0
    1414//
    1515
     
    2323namespace ast {
    2424
    25 class TranslationGlobal {
    26 public:
    27         std::map< UniqueId, Decl * > idMap;
    28 
    29         ptr<Type> sizeType;
    30         const FunctionDecl * dereference;
    31         const StructDecl * dtorStruct;
    32         const FunctionDecl * dtorDestroy;
    33 };
    34 
    3525class TranslationUnit {
    3626public:
    3727        std::list< ptr< Decl > > decls;
    38         TranslationGlobal global;
     28
     29        struct Global {
     30                std::map< UniqueId, Decl * > idMap;
     31
     32                ptr<Type> sizeType;
     33                const FunctionDecl * dereference;
     34                const StructDecl * dtorStruct;
     35                const FunctionDecl * dtorDestroy;
     36        } global;
    3937};
    4038
  • src/AST/Type.cpp

    r92538ab r4559b34  
    147147// --- TypeInstType
    148148
    149 TypeInstType::TypeInstType( const TypeDecl * b,
    150         CV::Qualifiers q, std::vector<ptr<Attribute>> && as )
    151 : BaseInstType( b->name, q, move(as) ), base( b ), kind( b->kind ) {}
    152 
    153149void TypeInstType::set_base( const TypeDecl * b ) {
    154150        base = b;
  • src/AST/Type.hpp

    r92538ab r4559b34  
    421421                std::vector<ptr<Attribute>> && as = {} )
    422422        : BaseInstType( n, q, std::move(as) ), base( b ), kind( b->kind ) {}
    423 
    424         TypeInstType( const TypeDecl * b,
    425                 CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
    426 
    427423        TypeInstType( const std::string& n, TypeDecl::Kind k, CV::Qualifiers q = {},
    428424                std::vector<ptr<Attribute>> && as = {} )
  • src/AST/TypeSubstitution.hpp

    r92538ab r4559b34  
    3737  public:
    3838        TypeSubstitution();
    39         template< typename FormalContainer, typename ActualContainer >
    40         TypeSubstitution( FormalContainer formals, ActualContainer actuals );
    4139        template< typename FormalIterator, typename ActualIterator >
    4240        TypeSubstitution( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin );
     
    7876        bool empty() const;
    7977
    80         template< typename FormalContainer, typename ActualContainer >
    81         void addAll( FormalContainer formals, ActualContainer actuals );
    8278        template< typename FormalIterator, typename ActualIterator >
    83         void addAll( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin );
     79        void add( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin );
    8480
    8581        /// create a new TypeSubstitution using bindings from env containing all of the type variables in expr
     
    116112};
    117113
    118 template< typename FormalContainer, typename ActualContainer >
    119 TypeSubstitution::TypeSubstitution( FormalContainer formals, ActualContainer actuals ) {
    120         assert( formals.size() == actuals.size() );
    121         addAll( formals.begin(), formals.end(), actuals.begin() );
    122 }
    123 
    124 template< typename FormalIterator, typename ActualIterator >
    125 TypeSubstitution::TypeSubstitution( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) {
    126         addAll( formalBegin, formalEnd, actualBegin );
    127 }
    128 
    129 template< typename FormalContainer, typename ActualContainer >
    130 void TypeSubstitution::addAll( FormalContainer formals, ActualContainer actuals ) {
    131         assert( formals.size() == actuals.size() );
    132         addAll( formals.begin(), formals.end(), actuals.begin() );
    133 }
    134 
    135114// this is the only place where type parameters outside a function formal may be substituted.
    136115template< typename FormalIterator, typename ActualIterator >
    137 void TypeSubstitution::addAll( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) {
     116void TypeSubstitution::add( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) {
    138117        // FormalIterator points to a TypeDecl
    139118        // ActualIterator points to a Type
     
    150129                        } // if
    151130                } else {
    152                         // Is this an error?
     131                       
    153132                } // if
    154133        } // for
    155134}
     135
     136
     137
     138template< typename FormalIterator, typename ActualIterator >
     139TypeSubstitution::TypeSubstitution( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) {
     140        add( formalBegin, formalEnd, actualBegin );
     141}
     142
    156143
    157144} // namespace ast
  • src/AST/Visitor.hpp

    r92538ab r4559b34  
    4141    virtual const ast::Stmt *             visit( const ast::ForStmt              * ) = 0;
    4242    virtual const ast::Stmt *             visit( const ast::SwitchStmt           * ) = 0;
    43     virtual const ast::CaseClause *       visit( const ast::CaseClause           * ) = 0;
     43    virtual const ast::Stmt *             visit( const ast::CaseStmt             * ) = 0;
    4444    virtual const ast::Stmt *             visit( const ast::BranchStmt           * ) = 0;
    4545    virtual const ast::Stmt *             visit( const ast::ReturnStmt           * ) = 0;
    4646    virtual const ast::Stmt *             visit( const ast::ThrowStmt            * ) = 0;
    4747    virtual const ast::Stmt *             visit( const ast::TryStmt              * ) = 0;
    48     virtual const ast::CatchClause *      visit( const ast::CatchClause          * ) = 0;
    49     virtual const ast::FinallyClause *    visit( const ast::FinallyClause        * ) = 0;
     48    virtual const ast::Stmt *             visit( const ast::CatchStmt            * ) = 0;
     49    virtual const ast::Stmt *             visit( const ast::FinallyStmt          * ) = 0;
    5050    virtual const ast::Stmt *             visit( const ast::SuspendStmt          * ) = 0;
    5151    virtual const ast::Stmt *             visit( const ast::WaitForStmt          * ) = 0;
  • src/AST/module.mk

    r92538ab r4559b34  
    1616
    1717SRC_AST = \
     18        AST/AssertAcyclic.cpp \
     19        AST/AssertAcyclic.hpp \
    1820        AST/Attribute.cpp \
    1921        AST/Attribute.hpp \
     
    6264        AST/TypeSubstitution.cpp \
    6365        AST/TypeSubstitution.hpp \
    64         AST/Util.cpp \
    65         AST/Util.hpp \
    6666        AST/Visitor.hpp
    6767
  • src/Common/CodeLocationTools.cpp

    r92538ab r4559b34  
    99// Author           : Andrew Beach
    1010// Created On       : Fri Dec  4 15:42:00 2020
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Mar 14 15:14:00 2022
    13 // Update Count     : 4
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Tue Feb  1 09:14:39 2022
     13// Update Count     : 3
    1414//
    1515
     
    112112    macro(ForStmt, Stmt) \
    113113    macro(SwitchStmt, Stmt) \
    114     macro(CaseClause, CaseClause) \
     114    macro(CaseStmt, Stmt) \
    115115    macro(BranchStmt, Stmt) \
    116116    macro(ReturnStmt, Stmt) \
    117117    macro(ThrowStmt, Stmt) \
    118118    macro(TryStmt, Stmt) \
    119     macro(CatchClause, CatchClause) \
    120     macro(FinallyClause, FinallyClause) \
     119    macro(CatchStmt, Stmt) \
     120    macro(FinallyStmt, Stmt) \
    121121    macro(SuspendStmt, Stmt) \
    122122    macro(WaitForStmt, Stmt) \
     
    239239};
    240240
    241 class LocalFillCore : public ast::WithGuards {
    242         CodeLocation const * parent;
    243 public:
    244         LocalFillCore( CodeLocation const & location ) : parent( &location ) {
    245                 assert( location.isSet() );
    246         }
    247 
    248         template<typename node_t>
    249         auto previsit( node_t const * node )
    250                         -> typename std::enable_if<has_code_location<node_t>::value, node_t const *>::type {
    251                 if ( node->location.isSet() ) {
    252                         GuardValue( parent ) = &node->location;
    253                         return node;
    254                 } else {
    255                         node_t * mut = ast::mutate( node );
    256                         mut->location = *parent;
    257                         return mut;
    258                 }
    259         }
    260 };
    261 
    262241} // namespace
    263242
     
    299278        ast::Pass<FillCore>::run( unit );
    300279}
    301 
    302 ast::Node const * localFillCodeLocations(
    303                 CodeLocation const & location , ast::Node const * node ) {
    304         ast::Pass<LocalFillCore> visitor( location );
    305         return node->accept( visitor );
    306 }
  • src/Common/CodeLocationTools.hpp

    r92538ab r4559b34  
    1010// Created On       : Fri Dec  4 15:35:00 2020
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Mar 14 15:14:00 2022
    13 // Update Count     : 2
     12// Last Modified On : Wed Dec  9  9:53:00 2020
     13// Update Count     : 1
    1414//
    1515
    1616#pragma once
    1717
    18 struct CodeLocation;
    1918namespace ast {
    20         class Node;
    2119        class TranslationUnit;
    2220}
     
    3028// Assign a nearby code-location to any unset code locations in the forest.
    3129void forceFillCodeLocations( ast::TranslationUnit & unit );
    32 
    33 // Fill in code-locations with a parent code location,
    34 // using the provided CodeLocation as the base.
    35 ast::Node const *
    36         localFillCodeLocations( CodeLocation const &, ast::Node const * );
  • src/Common/Examine.cc

    r92538ab r4559b34  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // Examine.cc -- Helpers for examining AST code.
     7// Examine.h --
    88//
    99// Author           : Andrew Beach
    1010// Created On       : Wed Sept 2 14:02 2020
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Dec 10 10:27 2021
    13 // Update Count     : 1
     12// Last Modified On : Wed Sep  8 12:15 2020
     13// Update Count     : 0
    1414//
    1515
    1616#include "Common/Examine.h"
    1717
    18 #include "AST/Type.hpp"
    1918#include "CodeGen/OperatorTable.h"
    20 #include "InitTweak/InitTweak.h"
    2119
    2220DeclarationWithType * isMainFor( FunctionDecl * func, AggregateDecl::Aggregate kind ) {
     
    3836
    3937namespace {
    40 
    41 // getTypeofThis but does some extra checks used in this module.
    42 const ast::Type * getTypeofThisSolo( const ast::FunctionDecl * func ) {
    43         if ( 1 != func->params.size() ) {
    44                 return nullptr;
    45         }
    46         auto ref = func->type->params.front().as<ast::ReferenceType>();
    47         return (ref) ? ref->base : nullptr;
    48 }
    49 
    50 }
    51 
    52 const ast::DeclWithType * isMainFor(
    53                 const ast::FunctionDecl * func, ast::AggregateDecl::Aggregate kind ) {
    54         if ( "main" != func->name ) return nullptr;
    55         if ( 1 != func->params.size() ) return nullptr;
    56 
    57         auto param = func->params.front();
    58 
    59         auto type = dynamic_cast<const ast::ReferenceType *>( param->get_type() );
    60         if ( !type ) return nullptr;
    61 
    62         auto obj = type->base.as<ast::StructInstType>();
    63         if ( !obj ) return nullptr;
    64 
    65         if ( kind != obj->base->kind ) return nullptr;
    66 
    67         return param;
    68 }
    69 
    70 namespace {
    7138        Type * getDestructorParam( FunctionDecl * func ) {
    7239                if ( !CodeGen::isDestructor( func->name ) ) return nullptr;
     
    8148                return nullptr;
    8249        }
    83 
    84 const ast::Type * getDestructorParam( const ast::FunctionDecl * func ) {
    85         if ( !CodeGen::isDestructor( func->name ) ) return nullptr;
    86         //return InitTweak::getParamThis( func )->type;
    87         return getTypeofThisSolo( func );
    88 }
    89 
    9050}
    9151
     
    9757        return false;
    9858}
    99 
    100 bool isDestructorFor(
    101                 const ast::FunctionDecl * func, const ast::StructDecl * type_decl ) {
    102         if ( const ast::Type * type = getDestructorParam( func ) ) {
    103                 auto stype = dynamic_cast<const ast::StructInstType *>( type );
    104                 return stype && stype->base.get() == type_decl;
    105         }
    106         return false;
    107 }
  • src/Common/Examine.h

    r92538ab r4559b34  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // Examine.h -- Helpers for examining AST code.
     7// Examine.h --
    88//
    99// Author           : Andrew Beach
    1010// Created On       : Wed Sept 2 13:57 2020
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Dec 10 10:28 2021
    13 // Update Count     : 1
     12// Last Modified On : Wed Sep  8 12:08 2020
     13// Update Count     : 0
    1414//
    1515
    16 #include "AST/Decl.hpp"
    1716#include "SynTree/Declaration.h"
    1817
    1918/// Check if this is a main function for a type of an aggregate kind.
    2019DeclarationWithType * isMainFor( FunctionDecl * func, AggregateDecl::Aggregate kind );
    21 const ast::DeclWithType * isMainFor(
    22         const ast::FunctionDecl * func, ast::AggregateDecl::Aggregate kind );
    2320// Returns a pointer to the parameter if true, nullptr otherwise.
    2421
    2522/// Check if this function is a destructor for the given structure.
    2623bool isDestructorFor( FunctionDecl * func, StructDecl * type_decl );
    27 bool isDestructorFor(
    28         const ast::FunctionDecl * func, const ast::StructDecl * type );
  • src/Concurrency/Keywords.cc

    r92538ab r4559b34  
    422422                        ;
    423423                else if ( auto param = isMainFor( decl, cast_target ) ) {
    424                         if ( !vtable_decl ) {
    425                                 SemanticError( decl, context_error );
    426                         }
     424                        // This should never trigger.
     425                        assert( vtable_decl );
    427426                        // Should be safe because of isMainFor.
    428427                        StructInstType * struct_type = static_cast<StructInstType *>(
     
    12041203                                        //new TypeofType( noQualifiers, args.front()->clone() )
    12051204                                        new TypeofType( noQualifiers, new UntypedExpr(
    1206                                                         new NameExpr( "__get_mutexstmt_lock_type" ),
     1205                                                        new NameExpr( "__get_type" ),
    12071206                                                        { args.front()->clone() }
    12081207                                                )
     
    12161215                                map_range < std::list<Initializer*> > ( args, [](Expression * var ){
    12171216                                        return new SingleInit( new UntypedExpr(
    1218                                                         new NameExpr( "__get_mutexstmt_lock_ptr" ),
     1217                                                        new NameExpr( "__get_ptr" ),
    12191218                                                        { var }
    12201219                                        ) );
     
    12271226                TypeExpr * lock_type_expr = new TypeExpr(
    12281227                        new TypeofType( noQualifiers, new UntypedExpr(
    1229                                 new NameExpr( "__get_mutexstmt_lock_type" ),
     1228                                new NameExpr( "__get_type" ),
    12301229                                { args.front()->clone() }
    12311230                                )
  • src/Concurrency/KeywordsNew.cpp

    r92538ab r4559b34  
    1010// Created On       : Tue Nov 16  9:53:00 2021
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Mar 11 10:40:00 2022
    13 // Update Count     : 2
     12// Last Modified On : Wed Dec  1 11:24:00 2021
     13// Update Count     : 1
    1414//
    15 
    16 #include <iostream>
    1715
    1816#include "Concurrency/Keywords.h"
     
    2018#include "AST/Copy.hpp"
    2119#include "AST/Decl.hpp"
    22 #include "AST/Expr.hpp"
    2320#include "AST/Pass.hpp"
    2421#include "AST/Stmt.hpp"
    25 #include "AST/DeclReplacer.hpp"
    2622#include "AST/TranslationUnit.hpp"
    2723#include "CodeGen/OperatorTable.h"
    28 #include "Common/Examine.h"
    2924#include "Common/utility.h"
    30 #include "Common/UniqueName.h"
    31 #include "ControlStruct/LabelGeneratorNew.hpp"
    3225#include "InitTweak/InitTweak.h"
    33 #include "Virtual/Tables.h"
    3426
    3527namespace Concurrency {
     
    3729namespace {
    3830
    39 // --------------------------------------------------------------------------
    40 // Loose Helper Functions:
    41 
    42 /// Detect threads constructed with the keyword thread.
    43 bool isThread( const ast::DeclWithType * decl ) {
     31inline static bool isThread( const ast::DeclWithType * decl ) {
    4432        auto baseType = decl->get_type()->stripDeclarator();
    4533        auto instType = dynamic_cast<const ast::StructInstType *>( baseType );
     
    4836}
    4937
    50 /// Get the virtual type id if given a type name.
    51 std::string typeIdType( std::string const & exception_name ) {
    52         return exception_name.empty() ? std::string()
    53                 : Virtual::typeIdType( exception_name );
    54 }
    55 
    56 /// Get the vtable type name if given a type name.
    57 std::string vtableTypeName( std::string const & exception_name ) {
    58         return exception_name.empty() ? std::string()
    59                 : Virtual::vtableTypeName( exception_name );
    60 }
    61 
    62 static ast::Type * mutate_under_references( ast::ptr<ast::Type>& type ) {
    63         ast::Type * mutType = type.get_and_mutate();
    64         for ( ast::ReferenceType * mutRef
    65                 ; (mutRef = dynamic_cast<ast::ReferenceType *>( mutType ))
    66                 ; mutType = mutRef->base.get_and_mutate() );
    67         return mutType;
    68 }
    69 
    70 // Describe that it adds the generic parameters and the uses of the generic
    71 // parameters on the function and first "this" argument.
    72 ast::FunctionDecl * fixupGenerics(
    73                 const ast::FunctionDecl * func, const ast::StructDecl * decl ) {
    74         const CodeLocation & location = decl->location;
    75         // We have to update both the declaration
    76         auto mutFunc = ast::mutate( func );
    77         auto mutType = mutFunc->type.get_and_mutate();
    78 
    79         if ( decl->params.empty() ) {
    80                 return mutFunc;
    81         }
    82 
    83         assert( 0 != mutFunc->params.size() );
    84         assert( 0 != mutType->params.size() );
    85 
    86         // Add the "forall" clause information.
    87         for ( const ast::ptr<ast::TypeDecl> & typeParam : decl->params ) {
    88                 auto typeDecl = ast::deepCopy( typeParam );
    89                 mutFunc->type_params.push_back( typeDecl );
    90                 mutType->forall.push_back( new ast::TypeInstType( typeDecl ) );
    91                 for ( auto & assertion : typeDecl->assertions ) {
    92                         mutFunc->assertions.push_back( assertion );
    93                         mutType->assertions.emplace_back(
    94                                 new ast::VariableExpr( location, assertion ) );
    95                 }
    96                 typeDecl->assertions.clear();
    97         }
    98 
    99         // Even chain_mutate is not powerful enough for this:
    100         ast::ptr<ast::Type>& paramType = strict_dynamic_cast<ast::ObjectDecl *>(
    101                 mutFunc->params[0].get_and_mutate() )->type;
    102         auto paramTypeInst = strict_dynamic_cast<ast::StructInstType *>(
    103                 mutate_under_references( paramType ) );
    104         auto typeParamInst = strict_dynamic_cast<ast::StructInstType *>(
    105                 mutate_under_references( mutType->params[0] ) );
    106 
    107         for ( const ast::ptr<ast::TypeDecl> & typeDecl : mutFunc->type_params ) {
    108                 paramTypeInst->params.push_back(
    109                         new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
    110                 typeParamInst->params.push_back(
    111                         new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
    112         }
    113 
    114         return mutFunc;
    115 }
    116 
    11738// --------------------------------------------------------------------------
    118 struct ConcurrentSueKeyword : public ast::WithDeclsToAdd<> {
    119         ConcurrentSueKeyword(
    120                 std::string&& type_name, std::string&& field_name,
    121                 std::string&& getter_name, std::string&& context_error,
    122                 std::string&& exception_name,
    123                 bool needs_main, ast::AggregateDecl::Aggregate cast_target
    124         ) :
    125                 type_name( type_name ), field_name( field_name ),
    126                 getter_name( getter_name ), context_error( context_error ),
    127                 exception_name( exception_name ),
    128                 typeid_name( typeIdType( exception_name ) ),
    129                 vtable_name( vtableTypeName( exception_name ) ),
    130                 needs_main( needs_main ), cast_target( cast_target )
    131         {}
    132 
    133         virtual ~ConcurrentSueKeyword() {}
    134 
    135         const ast::Decl * postvisit( const ast::StructDecl * decl );
    136         const ast::DeclWithType * postvisit( const ast::FunctionDecl * decl );
    137         const ast::Expr * postvisit( const ast::KeywordCastExpr * expr );
    138 
    139         struct StructAndField {
    140                 const ast::StructDecl * decl;
    141                 const ast::ObjectDecl * field;
    142         };
    143 
    144         const ast::StructDecl * handleStruct( const ast::StructDecl * );
    145         void handleMain( const ast::FunctionDecl *, const ast::StructInstType * );
    146         void addTypeId( const ast::StructDecl * );
    147         void addVtableForward( const ast::StructDecl * );
    148         const ast::FunctionDecl * forwardDeclare( const ast::StructDecl * );
    149         StructAndField addField( const ast::StructDecl * );
    150         void addGetRoutines( const ast::ObjectDecl *, const ast::FunctionDecl * );
    151         void addLockUnlockRoutines( const ast::StructDecl * );
    152 
    153 private:
    154         const std::string type_name;
    155         const std::string field_name;
    156         const std::string getter_name;
    157         const std::string context_error;
    158         const std::string exception_name;
    159         const std::string typeid_name;
    160         const std::string vtable_name;
    161         const bool needs_main;
    162         const ast::AggregateDecl::Aggregate cast_target;
    163 
    164         const ast::StructDecl   * type_decl = nullptr;
    165         const ast::FunctionDecl * dtor_decl = nullptr;
    166         const ast::StructDecl * except_decl = nullptr;
    167         const ast::StructDecl * typeid_decl = nullptr;
    168         const ast::StructDecl * vtable_decl = nullptr;
    169 
    170 };
    171 
    172 // Handles thread type declarations:
    173 //
    174 // thread Mythread {                         struct MyThread {
    175 //  int data;                                  int data;
    176 //  a_struct_t more_data;                      a_struct_t more_data;
    177 //                                =>             thread$ __thrd_d;
    178 // };                                        };
    179 //                                           static inline thread$ * get_thread( MyThread * this ) { return &this->__thrd_d; }
    180 //
    181 struct ThreadKeyword final : public ConcurrentSueKeyword {
    182         ThreadKeyword() : ConcurrentSueKeyword(
    183                 "thread$",
    184                 "__thrd",
    185                 "get_thread",
    186                 "thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
    187                 "ThreadCancelled",
    188                 true,
    189                 ast::AggregateDecl::Thread )
    190         {}
    191 
    192         virtual ~ThreadKeyword() {}
    193 };
    194 
    195 // Handles coroutine type declarations:
    196 //
    197 // coroutine MyCoroutine {                   struct MyCoroutine {
    198 //  int data;                                  int data;
    199 //  a_struct_t more_data;                      a_struct_t more_data;
    200 //                                =>             coroutine$ __cor_d;
    201 // };                                        };
    202 //                                           static inline coroutine$ * get_coroutine( MyCoroutine * this ) { return &this->__cor_d; }
    203 //
    204 struct CoroutineKeyword final : public ConcurrentSueKeyword {
    205         CoroutineKeyword() : ConcurrentSueKeyword(
    206                 "coroutine$",
    207                 "__cor",
    208                 "get_coroutine",
    209                 "coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n",
    210                 "CoroutineCancelled",
    211                 true,
    212                 ast::AggregateDecl::Coroutine )
    213         {}
    214 
    215         virtual ~CoroutineKeyword() {}
    216 };
    217 
    218 // Handles monitor type declarations:
    219 //
    220 // monitor MyMonitor {                       struct MyMonitor {
    221 //  int data;                                  int data;
    222 //  a_struct_t more_data;                      a_struct_t more_data;
    223 //                                =>             monitor$ __mon_d;
    224 // };                                        };
    225 //                                           static inline monitor$ * get_coroutine( MyMonitor * this ) {
    226 //                                               return &this->__cor_d;
    227 //                                           }
    228 //                                           void lock(MyMonitor & this) {
    229 //                                               lock(get_monitor(this));
    230 //                                           }
    231 //                                           void unlock(MyMonitor & this) {
    232 //                                               unlock(get_monitor(this));
    233 //                                           }
    234 //
    235 struct MonitorKeyword final : public ConcurrentSueKeyword {
    236         MonitorKeyword() : ConcurrentSueKeyword(
    237                 "monitor$",
    238                 "__mon",
    239                 "get_monitor",
    240                 "monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n",
    241                 "",
    242                 false,
    243                 ast::AggregateDecl::Monitor )
    244         {}
    245 
    246         virtual ~MonitorKeyword() {}
    247 };
    248 
    249 // Handles generator type declarations:
    250 //
    251 // generator MyGenerator {                   struct MyGenerator {
    252 //  int data;                                  int data;
    253 //  a_struct_t more_data;                      a_struct_t more_data;
    254 //                                =>             int __generator_state;
    255 // };                                        };
    256 //
    257 struct GeneratorKeyword final : public ConcurrentSueKeyword {
    258         GeneratorKeyword() : ConcurrentSueKeyword(
    259                 "generator$",
    260                 "__generator_state",
    261                 "get_generator",
    262                 "Unable to find builtin type generator$\n",
    263                 "",
    264                 true,
    265                 ast::AggregateDecl::Generator )
    266         {}
    267 
    268         virtual ~GeneratorKeyword() {}
    269 };
    270 
    271 const ast::Decl * ConcurrentSueKeyword::postvisit(
    272                 const ast::StructDecl * decl ) {
    273         if ( !decl->body ) {
    274                 return decl;
    275         } else if ( cast_target == decl->kind ) {
    276                 return handleStruct( decl );
    277         } else if ( type_name == decl->name ) {
    278                 assert( !type_decl );
    279                 type_decl = decl;
    280         } else if ( exception_name == decl->name ) {
    281                 assert( !except_decl );
    282                 except_decl = decl;
    283         } else if ( typeid_name == decl->name ) {
    284                 assert( !typeid_decl );
    285                 typeid_decl = decl;
    286         } else if ( vtable_name == decl->name ) {
    287                 assert( !vtable_decl );
    288                 vtable_decl = decl;
    289         }
    290         return decl;
    291 }
    292 
    293 // Try to get the full definition, but raise an error on conflicts.
    294 const ast::FunctionDecl * getDefinition(
    295                 const ast::FunctionDecl * old_decl,
    296                 const ast::FunctionDecl * new_decl ) {
    297         if ( !new_decl->stmts ) {
    298                 return old_decl;
    299         } else if ( !old_decl->stmts ) {
    300                 return new_decl;
    301         } else {
    302                 assert( !old_decl->stmts || !new_decl->stmts );
    303                 return nullptr;
    304         }
    305 }
    306 
    307 const ast::DeclWithType * ConcurrentSueKeyword::postvisit(
    308                 const ast::FunctionDecl * decl ) {
    309         if ( type_decl && isDestructorFor( decl, type_decl ) ) {
    310                 // Check for forward declarations, try to get the full definition.
    311                 dtor_decl = (dtor_decl) ? getDefinition( dtor_decl, decl ) : decl;
    312         } else if ( !vtable_name.empty() && decl->has_body() ) {
    313                 if (const ast::DeclWithType * param = isMainFor( decl, cast_target )) {
    314                         if ( !vtable_decl ) {
    315                                 SemanticError( decl, context_error );
    316                         }
    317                         // Should be safe because of isMainFor.
    318                         const ast::StructInstType * struct_type =
    319                                 static_cast<const ast::StructInstType *>(
    320                                         static_cast<const ast::ReferenceType *>(
    321                                                 param->get_type() )->base.get() );
    322 
    323                         handleMain( decl, struct_type );
    324                 }
    325         }
    326         return decl;
    327 }
    328 
    329 const ast::Expr * ConcurrentSueKeyword::postvisit(
    330                 const ast::KeywordCastExpr * expr ) {
    331         if ( cast_target == expr->target ) {
    332                 // Convert `(thread &)ex` to `(thread$ &)*get_thread(ex)`, etc.
    333                 if ( !type_decl || !dtor_decl ) {
    334                         SemanticError( expr, context_error );
    335                 }
    336                 assert( nullptr == expr->result );
    337                 auto cast = ast::mutate( expr );
    338                 cast->result = new ast::ReferenceType( new ast::StructInstType( type_decl ) );
    339                 cast->concrete_target.field  = field_name;
    340                 cast->concrete_target.getter = getter_name;
    341                 return cast;
    342         }
    343         return expr;
    344 }
    345 
    346 const ast::StructDecl * ConcurrentSueKeyword::handleStruct(
    347                 const ast::StructDecl * decl ) {
    348         assert( decl->body );
    349 
    350         if ( !type_decl || !dtor_decl ) {
    351                 SemanticError( decl, context_error );
    352         }
    353 
    354         if ( !exception_name.empty() ) {
    355                 if( !typeid_decl || !vtable_decl ) {
    356                         SemanticError( decl, context_error );
    357                 }
    358                 addTypeId( decl );
    359                 addVtableForward( decl );
    360         }
    361 
    362         const ast::FunctionDecl * func = forwardDeclare( decl );
    363         StructAndField addFieldRet = addField( decl );
    364         decl = addFieldRet.decl;
    365         const ast::ObjectDecl * field = addFieldRet.field;
    366 
    367         addGetRoutines( field, func );
    368         // Add routines to monitors for use by mutex stmt.
    369         if ( ast::AggregateDecl::Monitor == cast_target ) {
    370                 addLockUnlockRoutines( decl );
    371         }
    372 
    373         return decl;
    374 }
    375 
    376 void ConcurrentSueKeyword::handleMain(
    377                 const ast::FunctionDecl * decl, const ast::StructInstType * type ) {
    378         assert( vtable_decl );
    379         assert( except_decl );
    380 
    381         const CodeLocation & location = decl->location;
    382 
    383         std::vector<ast::ptr<ast::Expr>> poly_args = {
    384                 new ast::TypeExpr( location, type ),
    385         };
    386         ast::ObjectDecl * vtable_object = Virtual::makeVtableInstance(
    387                 location,
    388                 "_default_vtable_object_declaration",
    389                 new ast::StructInstType( vtable_decl, copy( poly_args ) ),
    390                 type,
    391                 nullptr
    392         );
    393         declsToAddAfter.push_back( vtable_object );
    394         declsToAddAfter.push_back(
    395                 new ast::ObjectDecl(
    396                         location,
    397                         Virtual::concurrentDefaultVTableName(),
    398                         new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
    399                         new ast::SingleInit( location,
    400                                 new ast::VariableExpr( location, vtable_object ) ),
    401                         ast::Storage::Classes(),
    402                         ast::Linkage::Cforall
    403                 )
    404         );
    405         declsToAddAfter.push_back( Virtual::makeGetExceptionFunction(
    406                 location,
    407                 vtable_object,
    408                 new ast::StructInstType( except_decl, copy( poly_args ) )
    409         ) );
    410 }
    411 
    412 void ConcurrentSueKeyword::addTypeId( const ast::StructDecl * decl ) {
    413         assert( typeid_decl );
    414         const CodeLocation & location = decl->location;
    415 
    416         ast::StructInstType * typeid_type =
    417                 new ast::StructInstType( typeid_decl, ast::CV::Const );
    418         typeid_type->params.push_back(
    419                 new ast::TypeExpr( location, new ast::StructInstType( decl ) ) );
    420         declsToAddBefore.push_back(
    421                 Virtual::makeTypeIdInstance( location, typeid_type ) );
    422         // If the typeid_type is going to be kept, the other reference will have
    423         // been made by now, but we also get to avoid extra mutates.
    424         ast::ptr<ast::StructInstType> typeid_cleanup = typeid_type;
    425 }
    426 
    427 void ConcurrentSueKeyword::addVtableForward( const ast::StructDecl * decl ) {
    428         assert( vtable_decl );
    429         const CodeLocation& location = decl->location;
    430 
    431         std::vector<ast::ptr<ast::Expr>> poly_args = {
    432                 new ast::TypeExpr( location, new ast::StructInstType( decl ) ),
    433         };
    434         declsToAddBefore.push_back( Virtual::makeGetExceptionForward(
    435                 location,
    436                 new ast::StructInstType( vtable_decl, copy( poly_args ) ),
    437                 new ast::StructInstType( except_decl, copy( poly_args ) )
    438         ) );
    439         ast::ObjectDecl * vtable_object = Virtual::makeVtableForward(
    440                 location,
    441                 "_default_vtable_object_declaration",
    442                 new ast::StructInstType( vtable_decl, std::move( poly_args ) )
    443         );
    444         declsToAddBefore.push_back( vtable_object );
    445         declsToAddBefore.push_back(
    446                 new ast::ObjectDecl(
    447                         location,
    448                         Virtual::concurrentDefaultVTableName(),
    449                         new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
    450                         nullptr,
    451                         ast::Storage::Extern,
    452                         ast::Linkage::Cforall
    453                 )
    454         );
    455 }
    456 
    457 const ast::FunctionDecl * ConcurrentSueKeyword::forwardDeclare(
    458                 const ast::StructDecl * decl ) {
    459         const CodeLocation & location = decl->location;
    460 
    461         ast::StructDecl * forward = ast::deepCopy( decl );
    462         {
    463                 // If removing members makes ref-count go to zero, do not free.
    464                 ast::ptr<ast::StructDecl> forward_ptr = forward;
    465                 forward->body = false;
    466                 forward->members.clear();
    467                 forward_ptr.release();
    468         }
    469 
    470         ast::ObjectDecl * this_decl = new ast::ObjectDecl(
    471                 location,
    472                 "this",
    473                 new ast::ReferenceType( new ast::StructInstType( decl ) ),
    474                 nullptr,
    475                 ast::Storage::Classes(),
    476                 ast::Linkage::Cforall
    477         );
    478 
    479         ast::ObjectDecl * ret_decl = new ast::ObjectDecl(
    480                 location,
    481                 "ret",
    482                 new ast::PointerType( new ast::StructInstType( type_decl ) ),
    483                 nullptr,
    484                 ast::Storage::Classes(),
    485                 ast::Linkage::Cforall
    486         );
    487 
    488         ast::FunctionDecl * get_decl = new ast::FunctionDecl(
    489                 location,
    490                 getter_name,
    491                 {}, // forall
    492                 { this_decl }, // params
    493                 { ret_decl }, // returns
    494                 nullptr, // stmts
    495                 ast::Storage::Static,
    496                 ast::Linkage::Cforall,
    497                 { new ast::Attribute( "const" ) },
    498                 ast::Function::Inline
    499         );
    500         get_decl = fixupGenerics( get_decl, decl );
    501 
    502         ast::FunctionDecl * main_decl = nullptr;
    503         if ( needs_main ) {
    504                 // `this_decl` is copied here because the original was used above.
    505                 main_decl = new ast::FunctionDecl(
    506                         location,
    507                         "main",
    508                         {},
    509                         { ast::deepCopy( this_decl ) },
    510                         {},
    511                         nullptr,
    512                         ast::Storage::Classes(),
    513                         ast::Linkage::Cforall
    514                 );
    515                 main_decl = fixupGenerics( main_decl, decl );
    516         }
    517 
    518         declsToAddBefore.push_back( forward );
    519         if ( needs_main ) declsToAddBefore.push_back( main_decl );
    520         declsToAddBefore.push_back( get_decl );
    521 
    522         return get_decl;
    523 }
    524 
    525 ConcurrentSueKeyword::StructAndField ConcurrentSueKeyword::addField(
    526                 const ast::StructDecl * decl ) {
    527         const CodeLocation & location = decl->location;
    528 
    529         ast::ObjectDecl * field = new ast::ObjectDecl(
    530                 location,
    531                 field_name,
    532                 new ast::StructInstType( type_decl ),
    533                 nullptr,
    534                 ast::Storage::Classes(),
    535                 ast::Linkage::Cforall
    536         );
    537 
    538         auto mutDecl = ast::mutate( decl );
    539         mutDecl->members.push_back( field );
    540 
    541         return {mutDecl, field};
    542 }
    543 
    544 void ConcurrentSueKeyword::addGetRoutines(
    545                 const ast::ObjectDecl * field, const ast::FunctionDecl * forward ) {
    546         // Say it is generated at the "same" places as the forward declaration.
    547         const CodeLocation & location = forward->location;
    548 
    549         const ast::DeclWithType * param = forward->params.front();
    550         ast::Stmt * stmt = new ast::ReturnStmt( location,
    551                 new ast::AddressExpr( location,
    552                         new ast::MemberExpr( location,
    553                                 field,
    554                                 new ast::CastExpr( location,
    555                                         new ast::VariableExpr( location, param ),
    556                                         ast::deepCopy( param->get_type()->stripReferences() ),
    557                                         ast::ExplicitCast
    558                                 )
    559                         )
    560                 )
    561         );
    562 
    563         ast::FunctionDecl * decl = ast::deepCopy( forward );
    564         decl->stmts = new ast::CompoundStmt( location, { stmt } );
    565         declsToAddAfter.push_back( decl );
    566 }
    567 
    568 void ConcurrentSueKeyword::addLockUnlockRoutines(
    569                 const ast::StructDecl * decl ) {
    570         // This should only be used on monitors.
    571         assert( ast::AggregateDecl::Monitor == cast_target );
    572 
    573         const CodeLocation & location = decl->location;
    574 
    575         // The parameter for both routines.
    576         ast::ObjectDecl * this_decl = new ast::ObjectDecl(
    577                 location,
    578                 "this",
    579                 new ast::ReferenceType( new ast::StructInstType( decl ) ),
    580                 nullptr,
    581                 ast::Storage::Classes(),
    582                 ast::Linkage::Cforall
    583         );
    584 
    585         ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
    586                 location,
    587                 "lock",
    588                 { /* forall */ },
    589                 {
    590                         // Copy the declaration of this.
    591                         ast::deepCopy( this_decl ),
    592                 },
    593                 { /* returns */ },
    594                 nullptr,
    595                 ast::Storage::Static,
    596                 ast::Linkage::Cforall,
    597                 { /* attributes */ },
    598                 ast::Function::Inline
    599         );
    600         lock_decl = fixupGenerics( lock_decl, decl );
    601 
    602         lock_decl->stmts = new ast::CompoundStmt( location, {
    603                 new ast::ExprStmt( location,
    604                         new ast::UntypedExpr( location,
    605                                 new ast::NameExpr( location, "lock" ),
    606                                 {
    607                                         new ast::UntypedExpr( location,
    608                                                 new ast::NameExpr( location, "get_monitor" ),
    609                                                 { new ast::VariableExpr( location,
    610                                                         InitTweak::getParamThis( lock_decl ) ) }
    611                                         )
    612                                 }
    613                         )
    614                 )
    615         } );
    616 
    617         ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
    618                 location,
    619                 "unlock",
    620                 { /* forall */ },
    621                 {
    622                         // Last use, consume the declaration of this.
    623                         this_decl,
    624                 },
    625                 { /* returns */ },
    626                 nullptr,
    627                 ast::Storage::Static,
    628                 ast::Linkage::Cforall,
    629                 { /* attributes */ },
    630                 ast::Function::Inline
    631         );
    632         unlock_decl = fixupGenerics( unlock_decl, decl );
    633 
    634         unlock_decl->stmts = new ast::CompoundStmt( location, {
    635                 new ast::ExprStmt( location,
    636                         new ast::UntypedExpr( location,
    637                                 new ast::NameExpr( location, "unlock" ),
    638                                 {
    639                                         new ast::UntypedExpr( location,
    640                                                 new ast::NameExpr( location, "get_monitor" ),
    641                                                 { new ast::VariableExpr( location,
    642                                                         InitTweak::getParamThis( unlock_decl ) ) }
    643                                         )
    644                                 }
    645                         )
    646                 )
    647         } );
    648 
    649         declsToAddAfter.push_back( lock_decl );
    650         declsToAddAfter.push_back( unlock_decl );
    651 }
    652 
    653 
    654 // --------------------------------------------------------------------------
    655 struct SuspendKeyword final :
    656                 public ast::WithStmtsToAdd<>, public ast::WithGuards {
    657         SuspendKeyword() = default;
    658         virtual ~SuspendKeyword() = default;
    659 
    660         void previsit( const ast::FunctionDecl * );
    661         const ast::DeclWithType * postvisit( const ast::FunctionDecl * );
    662         const ast::Stmt * postvisit( const ast::SuspendStmt * );
    663 
    664 private:
    665         bool is_real_suspend( const ast::FunctionDecl * );
    666 
    667         const ast::Stmt * make_generator_suspend( const ast::SuspendStmt * );
    668         const ast::Stmt * make_coroutine_suspend( const ast::SuspendStmt * );
    669 
    670         struct LabelPair {
    671                 ast::Label obj;
    672                 int idx;
    673         };
    674 
    675         LabelPair make_label(const ast::Stmt * stmt ) {
    676                 labels.push_back( ControlStruct::newLabel( "generator", stmt ) );
    677                 return { labels.back(), int(labels.size()) };
    678         }
    679 
    680         const ast::DeclWithType * in_generator = nullptr;
    681         const ast::FunctionDecl * decl_suspend = nullptr;
    682         std::vector<ast::Label> labels;
    683 };
    684 
    685 void SuspendKeyword::previsit( const ast::FunctionDecl * decl ) {
    686         GuardValue( in_generator ); in_generator = nullptr;
    687 
    688         // If it is the real suspend, grab it if we don't have one already.
    689         if ( is_real_suspend( decl ) ) {
    690                 decl_suspend = decl_suspend ? decl_suspend : decl;
    691                 return;
    692         }
    693 
    694         // Otherwise check if this is a generator main and, if so, handle it.
    695         auto param = isMainFor( decl, ast::AggregateDecl::Generator );
    696         if ( !param ) return;
    697 
    698         if ( 0 != decl->returns.size() ) {
    699                 SemanticError( decl->location, "Generator main must return void" );
    700         }
    701 
    702         in_generator = param;
    703         GuardValue( labels ); labels.clear();
    704 }
    705 
    706 const ast::DeclWithType * SuspendKeyword::postvisit(
    707                 const ast::FunctionDecl * decl ) {
    708         // Only modify a full definition of a generator with states.
    709         if ( !decl->stmts || !in_generator || labels.empty() ) return decl;
    710 
    711         const CodeLocation & location = decl->location;
    712 
    713         // Create a new function body:
    714         // static void * __generator_labels[] = {&&s0, &&s1, ...};
    715         // void * __generator_label = __generator_labels[GEN.__generator_state];
    716         // goto * __generator_label;
    717         // s0: ;
    718         // OLD_BODY
    719 
    720         // This is the null statement inserted right before the body.
    721         ast::NullStmt * noop = new ast::NullStmt( location );
    722         noop->labels.push_back( ControlStruct::newLabel( "generator", noop ) );
    723         const ast::Label & first_label = noop->labels.back();
    724 
    725         // Add each label to the init, starting with the first label.
    726         std::vector<ast::ptr<ast::Init>> inits = {
    727                 new ast::SingleInit( location,
    728                         new ast::LabelAddressExpr( location, copy( first_label ) ) ) };
    729         // Then go through all the stored labels, and clear the store.
    730         for ( auto && label : labels ) {
    731                 inits.push_back( new ast::SingleInit( label.location,
    732                         new ast::LabelAddressExpr( label.location, std::move( label )
    733                         ) ) );
    734         }
    735         labels.clear();
    736         // Then construct the initializer itself.
    737         auto init = new ast::ListInit( location, std::move( inits ) );
    738 
    739         ast::ObjectDecl * generatorLabels = new ast::ObjectDecl(
    740                 location,
    741                 "__generator_labels",
    742                 new ast::ArrayType(
    743                         new ast::PointerType( new ast::VoidType() ),
    744                         nullptr,
    745                         ast::FixedLen,
    746                         ast::DynamicDim
    747                 ),
    748                 init,
    749                 ast::Storage::Classes(),
    750                 ast::Linkage::AutoGen
    751         );
    752 
    753         ast::ObjectDecl * generatorLabel = new ast::ObjectDecl(
    754                 location,
    755                 "__generator_label",
    756                 new ast::PointerType( new ast::VoidType() ),
    757                 new ast::SingleInit( location,
    758                         new ast::UntypedExpr( location,
    759                                 new ast::NameExpr( location, "?[?]" ),
    760                                 {
    761                                         // TODO: Could be a variable expr.
    762                                         new ast::NameExpr( location, "__generator_labels" ),
    763                                         new ast::UntypedMemberExpr( location,
    764                                                 new ast::NameExpr( location, "__generator_state" ),
    765                                                 new ast::VariableExpr( location, in_generator )
    766                                         )
    767                                 }
    768                         )
    769                 ),
    770                 ast::Storage::Classes(),
    771                 ast::Linkage::AutoGen
    772         );
    773 
    774         ast::BranchStmt * theGoTo = new ast::BranchStmt(
    775                 location, new ast::VariableExpr( location, generatorLabel )
    776         );
    777 
    778         // The noop goes here in order.
    779 
    780         ast::CompoundStmt * body = new ast::CompoundStmt( location, {
    781                 { new ast::DeclStmt( location, generatorLabels ) },
    782                 { new ast::DeclStmt( location, generatorLabel ) },
    783                 { theGoTo },
    784                 { noop },
    785                 { decl->stmts },
    786         } );
    787 
    788         auto mutDecl = ast::mutate( decl );
    789         mutDecl->stmts = body;
    790         return mutDecl;
    791 }
    792 
    793 const ast::Stmt * SuspendKeyword::postvisit( const ast::SuspendStmt * stmt ) {
    794         switch ( stmt->type ) {
    795         case ast::SuspendStmt::None:
    796                 // Use the context to determain the implicit target.
    797                 if ( in_generator ) {
    798                         return make_generator_suspend( stmt );
    799                 } else {
    800                         return make_coroutine_suspend( stmt );
    801                 }
    802         case ast::SuspendStmt::Coroutine:
    803                 return make_coroutine_suspend( stmt );
    804         case ast::SuspendStmt::Generator:
    805                 // Generator suspends must be directly in a generator.
    806                 if ( !in_generator ) SemanticError( stmt->location, "'suspend generator' must be used inside main of generator type." );
    807                 return make_generator_suspend( stmt );
    808         }
    809         assert( false );
    810         return stmt;
    811 }
    812 
    813 /// Find the real/official suspend declaration.
    814 bool SuspendKeyword::is_real_suspend( const ast::FunctionDecl * decl ) {
    815         return ( !decl->linkage.is_mangled
    816                 && 0 == decl->params.size()
    817                 && 0 == decl->returns.size()
    818                 && "__cfactx_suspend" == decl->name );
    819 }
    820 
    821 const ast::Stmt * SuspendKeyword::make_generator_suspend(
    822                 const ast::SuspendStmt * stmt ) {
    823         assert( in_generator );
    824         // Target code is:
    825         //   GEN.__generator_state = X;
    826         //   THEN
    827         //   return;
    828         //   __gen_X:;
    829 
    830         const CodeLocation & location = stmt->location;
    831 
    832         LabelPair label = make_label( stmt );
    833 
    834         // This is the context saving statement.
    835         stmtsToAddBefore.push_back( new ast::ExprStmt( location,
    836                 new ast::UntypedExpr( location,
    837                         new ast::NameExpr( location, "?=?" ),
    838                         {
    839                                 new ast::UntypedMemberExpr( location,
    840                                         new ast::NameExpr( location, "__generator_state" ),
    841                                         new ast::VariableExpr( location, in_generator )
    842                                 ),
    843                                 ast::ConstantExpr::from_int( location, label.idx ),
    844                         }
    845                 )
    846         ) );
    847 
    848         // The THEN component is conditional (return is not).
    849         if ( stmt->then ) {
    850                 stmtsToAddBefore.push_back( stmt->then.get() );
    851         }
    852         stmtsToAddBefore.push_back( new ast::ReturnStmt( location, nullptr ) );
    853 
    854         // The null statement replaces the old suspend statement.
    855         return new ast::NullStmt( location, { label.obj } );
    856 }
    857 
    858 const ast::Stmt * SuspendKeyword::make_coroutine_suspend(
    859                 const ast::SuspendStmt * stmt ) {
    860         // The only thing we need from the old statement is the location.
    861         const CodeLocation & location = stmt->location;
    862 
    863         if ( !decl_suspend ) {
    864                 SemanticError( location, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>\n" );
    865         }
    866         if ( stmt->then ) {
    867                 SemanticError( location, "Compound statement following coroutines is not implemented." );
    868         }
    869 
    870         return new ast::ExprStmt( location,
    871                 new ast::UntypedExpr( location,
    872                         ast::VariableExpr::functionPointer( location, decl_suspend ) )
    873         );
    874 }
    875 
    876 // --------------------------------------------------------------------------
    877 struct MutexKeyword final : public ast::WithDeclsToAdd<> {
     39struct MutexKeyword final {
    87840        const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
    87941        void postvisit( const ast::StructDecl * decl );
     
    88850        ast::CompoundStmt * addStatements( const ast::CompoundStmt * body, const std::vector<ast::ptr<ast::Expr>> & args );
    88951        ast::CompoundStmt * addThreadDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt * body, const std::vector<const ast::DeclWithType *> & args );
    890         ast::ExprStmt * genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param);
    891         ast::IfStmt * genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam );
     52
    89253private:
    89354        const ast::StructDecl * monitor_decl = nullptr;
     
    89859
    89960        static ast::ptr<ast::Type> generic_func;
    900 
    901         UniqueName mutex_func_namer = UniqueName("__lock_unlock_curr");
    90261};
    90362
     
    1001160
    1002161const ast::Stmt * MutexKeyword::postvisit( const ast::MutexStmt * stmt ) {
    1003         if ( !lock_guard_decl ) {
    1004                 SemanticError( stmt->location, "mutex stmt requires a header, add #include <mutex_stmt.hfa>\n" );
    1005         }
    1006162        ast::CompoundStmt * body =
    1007163                        new ast::CompoundStmt( stmt->location, { stmt->stmt } );
    1008        
    1009         return addStatements( body, stmt->mutexObjs );;
     164        addStatements( body, stmt->mutexObjs );
     165        return body;
    1010166}
    1011167
     
    1095251                                {
    1096252                                        new ast::SingleInit( location,
    1097                                                 new ast::AddressExpr( location,
     253                                                new ast::AddressExpr(
    1098254                                                        new ast::VariableExpr( location, monitor ) ) ),
    1099255                                        new ast::SingleInit( location,
     
    1202358}
    1203359
    1204 // generates a cast to the void ptr to the appropriate lock type and dereferences it before calling lock or unlock on it
    1205 // used to undo the type erasure done by storing all the lock pointers as void
    1206 ast::ExprStmt * MutexKeyword::genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param ) {
    1207         return new ast::ExprStmt( location,
    1208                 new ast::UntypedExpr( location,
    1209                         new ast::NameExpr( location, fnName ), {
    1210                                 ast::UntypedExpr::createDeref(
    1211                                         location,
    1212                                         new ast::CastExpr( location,
    1213                                                 param,
    1214                                                 new ast::PointerType( new ast::TypeofType( new ast::UntypedExpr(
    1215                                                         expr->location,
    1216                                                         new ast::NameExpr( expr->location, "__get_mutexstmt_lock_type" ),
    1217                                                         { expr }
    1218                                                 ) ) ),
    1219                                                 ast::GeneratedFlag::ExplicitCast
    1220                                         )
    1221                                 )
    1222                         }
    1223                 )
    1224         );
    1225 }
    1226 
    1227 ast::IfStmt * MutexKeyword::genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam ) {
    1228         ast::IfStmt * outerLockIf = nullptr;
    1229         ast::IfStmt * lastLockIf = nullptr;
    1230 
    1231         //adds an if/elif clause for each lock to assign type from void ptr based on ptr address
    1232         for ( long unsigned int i = 0; i < args.size(); i++ ) {
    1233                
    1234                 ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
    1235                         new ast::NameExpr( location, "?==?" ), {
    1236                                 ast::deepCopy( thisParam ),
    1237                                 new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
    1238                         }
    1239                 );
    1240 
    1241                 ast::IfStmt * currLockIf = new ast::IfStmt(
    1242                         location,
    1243                         ifCond,
    1244                         genVirtLockUnlockExpr( fnName, args.at(i), location, ast::deepCopy( thisParam ) )
    1245                 );
    1246                
    1247                 if ( i == 0 ) {
    1248                         outerLockIf = currLockIf;
    1249                 } else {
    1250                         // add ifstmt to else of previous stmt
    1251                         lastLockIf->else_ = currLockIf;
    1252                 }
    1253 
    1254                 lastLockIf = currLockIf;
    1255         }
    1256         return outerLockIf;
    1257 }
    1258 
    1259360ast::CompoundStmt * MutexKeyword::addStatements(
    1260361                const ast::CompoundStmt * body,
    1261362                const std::vector<ast::ptr<ast::Expr>> & args ) {
     363        ast::CompoundStmt * mutBody = ast::mutate( body );
    1262364
    1263365        // Code is generated near the beginning of the compound statement.
    1264         const CodeLocation & location = body->location;
    1265 
    1266                 // final body to return
    1267         ast::CompoundStmt * newBody = new ast::CompoundStmt( location );
    1268 
    1269         // std::string lockFnName = mutex_func_namer.newName();
    1270         // std::string unlockFnName = mutex_func_namer.newName();
     366        const CodeLocation & location = mutBody->location;
    1271367
    1272368        // Make pointer to the monitors.
     
    1276372                new ast::ArrayType(
    1277373                        new ast::PointerType(
    1278                                 new ast::VoidType()
     374                                new ast::TypeofType(
     375                                        new ast::UntypedExpr(
     376                                                location,
     377                                                new ast::NameExpr( location, "__get_type" ),
     378                                                { args.front() }
     379                                        )
     380                                )
    1279381                        ),
    1280382                        ast::ConstantExpr::from_ulong( location, args.size() ),
     
    1290392                                                new ast::UntypedExpr(
    1291393                                                        expr->location,
    1292                                                         new ast::NameExpr( expr->location, "__get_mutexstmt_lock_ptr" ),
     394                                                        new ast::NameExpr( expr->location, "__get_ptr" ),
    1293395                                                        { expr }
    1294396                                                )
     
    1303405        ast::StructInstType * lock_guard_struct =
    1304406                        new ast::StructInstType( lock_guard_decl );
    1305 
    1306         // use try stmts to lock and finally to unlock
    1307         ast::TryStmt * outerTry = nullptr;
    1308         ast::TryStmt * currentTry;
    1309         ast::CompoundStmt * lastBody = nullptr;
    1310 
    1311         // adds a nested try stmt for each lock we are locking
    1312         for ( long unsigned int i = 0; i < args.size(); i++ ) {
    1313                 ast::UntypedExpr * innerAccess = new ast::UntypedExpr(
    1314                         location,
    1315                         new ast::NameExpr( location,"?[?]" ), {
    1316                                 new ast::NameExpr( location, "__monitors" ),
    1317                                 ast::ConstantExpr::from_int( location, i )
    1318                         }
    1319                 );
    1320 
    1321                 // make the try body
    1322                 ast::CompoundStmt * currTryBody = new ast::CompoundStmt( location );
    1323                 ast::IfStmt * lockCall = genTypeDiscrimLockUnlock( "lock", args, location, innerAccess );
    1324                 currTryBody->push_back( lockCall );
    1325 
    1326                 // make the finally stmt
    1327                 ast::CompoundStmt * currFinallyBody = new ast::CompoundStmt( location );
    1328                 ast::IfStmt * unlockCall = genTypeDiscrimLockUnlock( "unlock", args, location, innerAccess );
    1329                 currFinallyBody->push_back( unlockCall );
    1330 
    1331                 // construct the current try
    1332                 currentTry = new ast::TryStmt(
    1333                         location,
    1334                         currTryBody,
    1335                         {},
    1336                         new ast::FinallyClause( location, currFinallyBody )
    1337                 );
    1338                 if ( i == 0 ) outerTry = currentTry;
    1339                 else {
    1340                         // pushback try into the body of the outer try
    1341                         lastBody->push_back( currentTry );
    1342                 }
    1343                 lastBody = currTryBody;
    1344         }
    1345 
    1346         // push body into innermost try body
    1347         if ( lastBody != nullptr ) {
    1348                 lastBody->push_back( body );
    1349                 newBody->push_front( outerTry );
    1350         }
    1351 
     407        ast::TypeExpr * lock_type_expr = new ast::TypeExpr(
     408                location,
     409                new ast::TypeofType(
     410                        new ast::UntypedExpr(
     411                                location,
     412                                new ast::NameExpr( location, "__get_type" ),
     413                                { args.front() }
     414                        )
     415                )
     416        );
     417
     418        lock_guard_struct->params.push_back( lock_type_expr );
     419
     420        // In reverse order:
    1352421        // monitor_guard_t __guard = { __monitors, # };
    1353         newBody->push_front(
     422        mutBody->push_front(
    1354423                new ast::DeclStmt(
    1355424                        location,
     
    1378447
    1379448        // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
    1380         newBody->push_front( new ast::DeclStmt( location, monitors ) );
    1381 
    1382         // // The parameter for both __lock_curr/__unlock_curr routines.
    1383         // ast::ObjectDecl * this_decl = new ast::ObjectDecl(
    1384         //      location,
    1385         //      "this",
    1386         //      new ast::PointerType( new ast::VoidType() ),
    1387         //      nullptr,
    1388         //      {},
    1389         //      ast::Linkage::Cforall
    1390         // );
    1391 
    1392         // ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
    1393         //      location,
    1394         //      lockFnName,
    1395         //      { /* forall */ },
    1396         //      {
    1397         //              // Copy the declaration of this.
    1398         //              this_decl,
    1399         //      },
    1400         //      { /* returns */ },
    1401         //      nullptr,
    1402         //      0,
    1403         //      ast::Linkage::Cforall,
    1404         //      { /* attributes */ },
    1405         //      ast::Function::Inline
    1406         // );
    1407 
    1408         // ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
    1409         //      location,
    1410         //      unlockFnName,
    1411         //      { /* forall */ },
    1412         //      {
    1413         //              // Copy the declaration of this.
    1414         //              ast::deepCopy( this_decl ),
    1415         //      },
    1416         //      { /* returns */ },
    1417         //      nullptr,
    1418         //      0,
    1419         //      ast::Linkage::Cforall,
    1420         //      { /* attributes */ },
    1421         //      ast::Function::Inline
    1422         // );
    1423 
    1424         // ast::IfStmt * outerLockIf = nullptr;
    1425         // ast::IfStmt * outerUnlockIf = nullptr;
    1426         // ast::IfStmt * lastLockIf = nullptr;
    1427         // ast::IfStmt * lastUnlockIf = nullptr;
    1428 
    1429         // //adds an if/elif clause for each lock to assign type from void ptr based on ptr address
    1430         // for ( long unsigned int i = 0; i < args.size(); i++ ) {
    1431         //      ast::VariableExpr * thisParam = new ast::VariableExpr( location, InitTweak::getParamThis( lock_decl ) );
    1432         //      ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
    1433         //              new ast::NameExpr( location, "?==?" ), {
    1434         //                      thisParam,
    1435         //                      new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
    1436         //              }
    1437         //      );
    1438 
    1439         //      ast::IfStmt * currLockIf = new ast::IfStmt(
    1440         //              location,
    1441         //              ast::deepCopy( ifCond ),
    1442         //              genVirtLockUnlockExpr( "lock", args.at(i), location, ast::deepCopy( thisParam ) )
    1443         //      );
    1444 
    1445         //      ast::IfStmt * currUnlockIf = new ast::IfStmt(
    1446         //              location,
    1447         //              ifCond,
    1448         //              genVirtLockUnlockExpr( "unlock", args.at(i), location, ast::deepCopy( thisParam ) )
    1449         //      );
    1450                
    1451         //      if ( i == 0 ) {
    1452         //              outerLockIf = currLockIf;
    1453         //              outerUnlockIf = currUnlockIf;
    1454         //      } else {
    1455         //              // add ifstmt to else of previous stmt
    1456         //              lastLockIf->else_ = currLockIf;
    1457         //              lastUnlockIf->else_ = currUnlockIf;
    1458         //      }
    1459 
    1460         //      lastLockIf = currLockIf;
    1461         //      lastUnlockIf = currUnlockIf;
    1462         // }
    1463        
    1464         // // add pointer typing if/elifs to body of routines
    1465         // lock_decl->stmts = new ast::CompoundStmt( location, { outerLockIf } );
    1466         // unlock_decl->stmts = new ast::CompoundStmt( location, { outerUnlockIf } );
    1467 
    1468         // // add routines to scope
    1469         // declsToAddBefore.push_back( lock_decl );
    1470         // declsToAddBefore.push_back( unlock_decl );
    1471 
    1472         // newBody->push_front(new ast::DeclStmt( location, lock_decl ));
    1473         // newBody->push_front(new ast::DeclStmt( location, unlock_decl ));
    1474 
    1475         return newBody;
     449        mutBody->push_front( new ast::DeclStmt( location, monitors ) );
     450
     451        return mutBody;
    1476452}
    1477453
     
    1588564
    1589565// --------------------------------------------------------------------------
    1590 // Interface Functions:
    1591566
    1592567void implementKeywords( ast::TranslationUnit & translationUnit ) {
    1593         ast::Pass<ThreadKeyword>::run( translationUnit );
    1594         ast::Pass<CoroutineKeyword>::run( translationUnit );
    1595         ast::Pass<MonitorKeyword>::run( translationUnit );
    1596         ast::Pass<GeneratorKeyword>::run( translationUnit );
    1597         ast::Pass<SuspendKeyword>::run( translationUnit );
     568        (void)translationUnit;
     569        assertf(false, "Apply Keywords not implemented." );
    1598570}
    1599571
  • src/ControlStruct/ExceptTranslateNew.cpp

    r92538ab r4559b34  
    99// Author           : Andrew Beach
    1010// Created On       : Mon Nov  8 11:53:00 2021
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Mar 11 17:51:00 2022
    13 // Update Count     : 2
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Mon Jan 31 18:49:58 2022
     13// Update Count     : 1
    1414//
    1515
     
    2626namespace {
    2727
    28         typedef std::list<ast::CatchClause*> CatchList;
     28        typedef std::list<ast::CatchStmt*> CatchList;
     29
     30        void split( CatchList& allHandlers, CatchList& terHandlers,
     31                                CatchList& resHandlers ) {
     32                while ( !allHandlers.empty() ) {
     33                        ast::CatchStmt * stmt = allHandlers.front();
     34                        allHandlers.pop_front();
     35                        if (stmt->kind == ast::ExceptionKind::Terminate) {
     36                                terHandlers.push_back(stmt);
     37                        } else {
     38                                resHandlers.push_back(stmt);
     39                        }
     40                }
     41        }
    2942
    3043        void appendDeclStmt( ast::CompoundStmt * block, ast::DeclWithType * item ) {
     
    4558        {}
    4659
    47         void previsit( const ast::CatchClause * stmt );
     60        void previsit( const ast::CatchStmt * stmt );
    4861        const ast::Stmt * postvisit( const ast::ThrowStmt * stmt );
    4962};
     
    88101}
    89102
    90 void TranslateThrowsCore::previsit( const ast::CatchClause * stmt ) {
     103void TranslateThrowsCore::previsit( const ast::CatchStmt * stmt ) {
    91104        // Validate the statement's form.
    92105        const ast::ObjectDecl * decl = stmt->decl.as<ast::ObjectDecl>();
     
    147160        ast::FunctionDecl * create_terminate_catch( CatchList &handlers );
    148161        ast::CompoundStmt * create_single_matcher(
    149                 const ast::DeclWithType * except_obj, ast::CatchClause * modded_handler );
     162                const ast::DeclWithType * except_obj, ast::CatchStmt * modded_handler );
    150163        ast::FunctionDecl * create_terminate_match( CatchList &handlers );
    151164        ast::CompoundStmt * create_terminate_caller( CodeLocation loc, ast::FunctionDecl * try_wrapper,
     
    158171        ast::Stmt * create_resume_rethrow( const ast::ThrowStmt * throwStmt );
    159172
    160         // Types used in translation, first group are internal.
    161         ast::ObjectDecl * make_index_object( CodeLocation const & ) const;
    162         ast::ObjectDecl * make_exception_object( CodeLocation const & ) const;
    163         ast::ObjectDecl * make_bool_object( CodeLocation const & ) const;
    164         ast::ObjectDecl * make_voidptr_object( CodeLocation const & ) const;
    165         ast::ObjectDecl * make_unused_index_object( CodeLocation const & ) const;
     173        // Types used in translation, make sure to use clone.
    166174        // void (*function)();
    167         ast::FunctionDecl * make_try_function( CodeLocation const & ) const;
     175        ast::FunctionDecl * try_func_t;
    168176        // void (*function)(int, exception);
    169         ast::FunctionDecl * make_catch_function( CodeLocation const & ) const;
     177        ast::FunctionDecl * catch_func_t;
    170178        // int (*function)(exception);
    171         ast::FunctionDecl * make_match_function( CodeLocation const & ) const;
     179        ast::FunctionDecl * match_func_t;
    172180        // bool (*function)(exception);
    173         ast::FunctionDecl * make_handle_function( CodeLocation const & ) const;
     181        ast::FunctionDecl * handle_func_t;
    174182        // void (*function)(__attribute__((unused)) void *);
    175         ast::FunctionDecl * make_finally_function( CodeLocation const & ) const;
     183        ast::FunctionDecl * finally_func_t;
     184
     185        ast::StructInstType * create_except_type() {
     186                assert( except_decl );
     187                return new ast::StructInstType( except_decl );
     188        }
     189        void init_func_types();
    176190
    177191public:
     
    185199};
    186200
    187 ast::ObjectDecl * TryMutatorCore::make_index_object(
    188                 CodeLocation const & location ) const {
    189         return new ast::ObjectDecl(
    190                 location,
     201void TryMutatorCore::init_func_types() {
     202        assert( except_decl );
     203
     204        ast::ObjectDecl index_obj(
     205                {},
    191206                "__handler_index",
    192                 new ast::BasicType(ast::BasicType::SignedInt),
    193                 nullptr, //init
    194                 ast::Storage::Classes{},
    195                 ast::Linkage::Cforall
    196                 );
    197 }
    198 
    199 ast::ObjectDecl * TryMutatorCore::make_exception_object(
    200                 CodeLocation const & location ) const {
    201         assert( except_decl );
    202         return new ast::ObjectDecl(
    203                 location,
     207                new ast::BasicType(ast::BasicType::SignedInt)
     208                );
     209        ast::ObjectDecl exception_obj(
     210                {},
    204211                "__exception_inst",
    205212                new ast::PointerType(
    206213                        new ast::StructInstType( except_decl )
    207214                        ),
    208                 nullptr, //init
    209                 ast::Storage::Classes{},
    210                 ast::Linkage::Cforall
    211                 );
    212 }
    213 
    214 ast::ObjectDecl * TryMutatorCore::make_bool_object(
    215                 CodeLocation const & location ) const {
    216         return new ast::ObjectDecl(
    217                 location,
     215                NULL
     216                );
     217        ast::ObjectDecl bool_obj(
     218                {},
    218219                "__ret_bool",
    219220                new ast::BasicType( ast::BasicType::Bool ),
     
    224225                std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
    225226                );
    226 }
    227 
    228 ast::ObjectDecl * TryMutatorCore::make_voidptr_object(
    229                 CodeLocation const & location ) const {
    230         return new ast::ObjectDecl(
    231                 location,
     227        ast::ObjectDecl voidptr_obj(
     228                {},
    232229                "__hook",
    233230                new ast::PointerType(
     
    240237                std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
    241238                );
    242 }
    243 
    244 ast::ObjectDecl * TryMutatorCore::make_unused_index_object(
    245                 CodeLocation const & location ) const {
    246         return new ast::ObjectDecl(
    247                 location,
     239
     240        ast::ObjectDecl unused_index_obj(
     241                {},
    248242                "__handler_index",
    249243                new ast::BasicType(ast::BasicType::SignedInt),
     
    254248                std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
    255249        );
    256 }
    257 
    258 ast::FunctionDecl * TryMutatorCore::make_try_function(
    259                 CodeLocation const & location ) const {
    260         return new ast::FunctionDecl(
    261                 location,
     250        //unused_index_obj->attributes.push_back( new Attribute( "unused" ) );
     251
     252        try_func_t = new ast::FunctionDecl(
     253                {},
    262254                "try",
    263255                {}, //forall
     
    268260                ast::Linkage::Cforall
    269261        );
    270 }
    271 
    272 ast::FunctionDecl * TryMutatorCore::make_catch_function(
    273                 CodeLocation const & location ) const {
    274         return new ast::FunctionDecl(
    275                 location,
     262
     263        catch_func_t = new ast::FunctionDecl(
     264                {},
    276265                "catch",
    277266                {}, //forall
    278                 { make_index_object( location ), make_exception_object( location ) },
     267                {ast::deepCopy(&index_obj), ast::deepCopy(&exception_obj)},//param
    279268                {}, //return void
    280269                nullptr,
     
    282271                ast::Linkage::Cforall
    283272        );
    284 }
    285 
    286 ast::FunctionDecl * TryMutatorCore::make_match_function(
    287                 CodeLocation const & location ) const {
    288         return new ast::FunctionDecl(
    289                 location,
     273
     274        match_func_t = new ast::FunctionDecl(
     275                {},
    290276                "match",
    291277                {}, //forall
    292                 { make_exception_object( location ) },
    293                 { make_unused_index_object( location ) },
     278                {ast::deepCopy(&exception_obj)},
     279                {ast::deepCopy(&unused_index_obj)},
    294280                nullptr,
    295281                ast::Storage::Classes{},
    296282                ast::Linkage::Cforall
    297283        );
    298 }
    299 
    300 ast::FunctionDecl * TryMutatorCore::make_handle_function(
    301                 CodeLocation const & location ) const {
    302         return new ast::FunctionDecl(
    303                 location,
     284
     285        handle_func_t = new ast::FunctionDecl(
     286                {},
    304287                "handle",
    305288                {}, //forall
    306                 { make_exception_object( location ) },
    307                 { make_bool_object( location ) },
     289                {ast::deepCopy(&exception_obj)},
     290                {ast::deepCopy(&bool_obj)},
    308291                nullptr,
    309292                ast::Storage::Classes{},
    310293                ast::Linkage::Cforall
    311294        );
    312 }
    313 
    314 ast::FunctionDecl * TryMutatorCore::make_finally_function(
    315                 CodeLocation const & location ) const {
    316         return new ast::FunctionDecl(
    317                 location,
     295
     296        finally_func_t = new ast::FunctionDecl(
     297                {},
    318298                "finally",
    319299                {}, //forall
    320                 { make_voidptr_object( location ) },
     300                {ast::deepCopy(&voidptr_obj)},
    321301                {}, //return void
    322302                nullptr,
     
    324304                ast::Linkage::Cforall
    325305        );
     306
     307        //catch_func_t.get_parameters().push_back( index_obj.clone() );
     308        //catch_func_t.get_parameters().push_back( exception_obj.clone() );
     309        //match_func_t.get_returnVals().push_back( unused_index_obj );
     310        //match_func_t.get_parameters().push_back( exception_obj.clone() );
     311        //handle_func_t.get_returnVals().push_back( bool_obj.clone() );
     312        //handle_func_t.get_parameters().push_back( exception_obj.clone() );
     313        //finally_func_t.get_parameters().push_back( voidptr_obj.clone() );
    326314}
    327315
    328316// TryStmt Mutation Helpers
     317
     318/*
     319ast::CompoundStmt * TryMutatorCore::take_try_block( ast::TryStmt *tryStmt ) {
     320        ast::CompoundStmt * block = tryStmt->body;
     321        tryStmt->body = nullptr;
     322        return block;
     323}
     324*/
    329325
    330326ast::FunctionDecl * TryMutatorCore::create_try_wrapper(
    331327                const ast::CompoundStmt *body ) {
    332328
    333         ast::FunctionDecl * ret = make_try_function( body->location );
     329        ast::FunctionDecl * ret = ast::deepCopy(try_func_t);
    334330        ret->stmts = body;
    335331        return ret;
     
    338334ast::FunctionDecl * TryMutatorCore::create_terminate_catch(
    339335                CatchList &handlers ) {
    340         std::vector<ast::ptr<ast::CaseClause>> handler_wrappers;
     336        std::vector<ast::ptr<ast::Stmt>> handler_wrappers;
    341337
    342338        assert (!handlers.empty());
    343339        const CodeLocation loc = handlers.front()->location;
    344340
    345         ast::FunctionDecl * func_t = make_catch_function( loc );
     341        ast::FunctionDecl * func_t = ast::deepCopy(catch_func_t);
    346342        const ast::DeclWithType * index_obj = func_t->params.front();
    347343        const ast::DeclWithType * except_obj = func_t->params.back();
     
    352348        for ( ; it != handlers.end() ; ++it ) {
    353349                ++index;
    354                 ast::CatchClause * handler = *it;
     350                ast::CatchStmt * handler = *it;
    355351                const CodeLocation loc = handler->location;
    356352
     
    390386                // handler->body = nullptr;
    391387
    392                 handler_wrappers.push_back( new ast::CaseClause(loc,
     388                handler_wrappers.push_back( new ast::CaseStmt(loc,
    393389                        ast::ConstantExpr::from_int(loc, index) ,
    394390                        { block, new ast::ReturnStmt( loc, nullptr ) }
     
    397393        // TODO: Some sort of meaningful error on default perhaps?
    398394
    399         ast::SwitchStmt * handler_lookup = new ast::SwitchStmt( loc,
     395        /*
     396        std::list<Statement*> stmt_handlers;
     397        while ( !handler_wrappers.empty() ) {
     398                stmt_handlers.push_back( handler_wrappers.front() );
     399                handler_wrappers.pop_front();
     400        }
     401        */
     402
     403        ast::SwitchStmt * handler_lookup = new ast::SwitchStmt(loc,
    400404                new ast::VariableExpr( loc, index_obj ),
    401405                std::move(handler_wrappers)
    402406                );
    403         ast::CompoundStmt * body = new ast::CompoundStmt( loc, {handler_lookup} );
     407        ast::CompoundStmt * body = new ast::CompoundStmt(loc,
     408                {handler_lookup});
    404409
    405410        func_t->stmts = body;
     
    410415// except_obj is referenced, modded_handler will be freed.
    411416ast::CompoundStmt * TryMutatorCore::create_single_matcher(
    412                 const ast::DeclWithType * except_obj, ast::CatchClause * modded_handler ) {
     417                const ast::DeclWithType * except_obj, ast::CatchStmt * modded_handler ) {
    413418        // {
    414419        //     `modded_handler.decl`
     
    428433
    429434        // Check for type match.
    430         ast::VirtualCastExpr * vcex = new ast::VirtualCastExpr(loc,
     435        ast::VirtualCastExpr * vcex = new ast::VirtualCastExpr(loc, 
    431436                new ast::VariableExpr(loc, except_obj ),
    432437                local_except->get_type()
     
    440445        }
    441446        // Construct the match condition.
    442         block->push_back( new ast::IfStmt(loc,
     447        block->push_back( new ast::IfStmt(loc, 
    443448                cond, modded_handler->body, nullptr ) );
    444449
     450        // xxx - how does this work in new ast
     451        //modded_handler->set_decl( nullptr );
     452        //modded_handler->set_cond( nullptr );
     453        //modded_handler->set_body( nullptr );
     454        //delete modded_handler;
    445455        return block;
    446456}
     
    457467        ast::CompoundStmt * body = new ast::CompoundStmt(loc);
    458468
    459         ast::FunctionDecl * func_t = make_match_function( loc );
     469        ast::FunctionDecl * func_t = ast::deepCopy(match_func_t);
    460470        const ast::DeclWithType * except_obj = func_t->params.back();
    461471
     
    465475        for ( it = handlers.begin() ; it != handlers.end() ; ++it ) {
    466476                ++index;
    467                 ast::CatchClause * handler = *it;
     477                ast::CatchStmt * handler = *it;
    468478
    469479                // Body should have been taken by create_terminate_catch.
     
    480490        }
    481491
    482         body->push_back( new ast::ReturnStmt(loc,
     492        body->push_back( new ast::ReturnStmt(loc, 
    483493                ast::ConstantExpr::from_int( loc, 0 ) ));
    484494
     
    515525        ast::CompoundStmt * body = new ast::CompoundStmt(loc);
    516526
    517         ast::FunctionDecl * func_t = make_handle_function( loc );
     527        ast::FunctionDecl * func_t = ast::deepCopy(handle_func_t);
    518528        const ast::DeclWithType * except_obj = func_t->params.back();
    519529
    520530        CatchList::iterator it;
    521531        for ( it = handlers.begin() ; it != handlers.end() ; ++it ) {
    522                 ast::CatchClause * handler = *it;
     532                ast::CatchStmt * handler = *it;
    523533                const CodeLocation loc = handler->location;
    524534                // Modifiy body.
    525535                ast::CompoundStmt * handling_code;
    526536                if (handler->body.as<ast::CompoundStmt>()) {
    527                         handling_code = strict_dynamic_cast<ast::CompoundStmt*>(
    528                                 handler->body.get_and_mutate() );
     537                        handling_code =
     538                        strict_dynamic_cast<ast::CompoundStmt*>( handler->body.get_and_mutate() );
    529539                } else {
    530540                        handling_code = new ast::CompoundStmt(loc);
     
    587597                ast::TryStmt * tryStmt ) {
    588598        // void finally() { `finally->block` }
    589         const ast::FinallyClause * finally = tryStmt->finally;
     599        const ast::FinallyStmt * finally = tryStmt->finally;
    590600        const ast::CompoundStmt * body = finally->body;
    591601
    592         ast::FunctionDecl * func_t = make_finally_function( tryStmt->location );
     602        ast::FunctionDecl * func_t = ast::deepCopy(finally_func_t);
    593603        func_t->stmts = body;
    594604
     605        // finally->set_block( nullptr );
     606        // delete finally;
    595607        tryStmt->finally = nullptr;
     608
    596609
    597610        return func_t;
     
    604617
    605618        const CodeLocation loc = finally_wrapper->location;
     619        // Make Cleanup Attribute.
     620        /*
     621        std::list< ast::Attribute * > attributes;
     622        {
     623                std::list<  > attr_params;
     624                attr_params.push_back( nameOf( finally_wrapper ) );
     625                attributes.push_back( new Attribute( "cleanup", attr_params ) );
     626        }
     627        */
     628
    606629        return new ast::ObjectDecl(
    607630                loc,
     
    621644        // return false;
    622645        const CodeLocation loc = throwStmt->location;
    623         ast::Stmt * result = new ast::ReturnStmt(loc,
     646        ast::Stmt * result = new ast::ReturnStmt(loc, 
    624647                ast::ConstantExpr::from_bool( loc, false )
    625648                );
    626649        result->labels = throwStmt->labels;
     650        // delete throwStmt; done by postvisit
    627651        return result;
    628652}
     
    636660                assert( nullptr == except_decl );
    637661                except_decl = structDecl;
     662                init_func_types();
    638663        } else if ( structDecl->name == "__cfaehm_try_resume_node" ) {
    639664                assert( nullptr == node_decl );
     
    681706                }
    682707        }
     708        // split( mutStmt->handlers,
     709        //              termination_handlers, resumption_handlers );
    683710
    684711        if ( resumption_handlers.size() ) {
  • src/ControlStruct/LabelGeneratorNew.cpp

    r92538ab r4559b34  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // LabelGeneratorNew.cpp --
     7// LabelGenerator.cc --
    88//
    99// Author           : Peter A. Buhr
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Mar 28 10:03:00 2022
    13 // Update Count     : 73
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Wed Feb  2 09:11:17 2022
     13// Update Count     : 72
    1414//
    1515
     
    2525namespace ControlStruct {
    2626
    27 enum { size = 128 };
    28 
    29 static int newLabelPre( char buf[size], const string & suffix ) {
     27Label newLabel( const string & suffix, const Stmt * stmt ) {
    3028        static int current = 0;
    3129
     30        assertf( stmt, "CFA internal error: parameter statement cannot be null pointer" );
     31
     32        enum { size = 128 };
     33        char buf[size];                                                                         // space to build label
    3234        int len = snprintf( buf, size, "__L%d__%s", current++, suffix.c_str() );
    3335        assertf( len < size, "CFA Internal error: buffer overflow creating label" );
    34         return len;
    35 }
    36 
    37 static Label newLabelPost( char buf[size], const CodeLocation & location ) {
    38         Label ret_label( location, buf );
    39         ret_label.attributes.push_back( new Attribute( "unused" ) );
    40         return ret_label;
    41 }
    42 
    43 Label newLabel( const string & suffix, const Stmt * stmt ) {
    44         // Buffer for string manipulation.
    45         char buf[size];
    46 
    47         assertf( stmt, "CFA internal error: parameter statement cannot be null pointer" );
    48         int len = newLabelPre( buf, suffix );
    4936
    5037        // What does this do?
     
    5441        } // if
    5542
    56         return newLabelPost( buf, stmt->location );
    57 }
    58 
    59 Label newLabel( const string & suffix, const CodeLocation & location ) {
    60         // Buffer for string manipulation.
    61         char buf[size];
    62 
    63         newLabelPre( buf, suffix );
    64         return newLabelPost( buf, location );
     43        Label ret_label( stmt->location, buf );
     44        ret_label.attributes.push_back( new Attribute( "unused" ) );
     45        return ret_label;
    6546}
    6647
  • src/ControlStruct/LabelGeneratorNew.hpp

    r92538ab r4559b34  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Fir Mar 25 15:40:00 2022
    13 // Update Count     : 28
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Mon Jan 31 18:03:09 2022
     13// Update Count     : 27
    1414//
    1515
     
    1818#include <string>                                                                               // for string
    1919
    20 class CodeLocation;
     20class Statement;
    2121
    2222namespace ast {
     23        class Stmt;
    2324        class Label;
    24         class Stmt;
    2525} // namespace ast
    2626
    2727namespace ControlStruct {
    2828        ast::Label newLabel( const std::string &, const ast::Stmt * );
    29         ast::Label newLabel( const std::string &, const CodeLocation & );
    3029} // namespace ControlStruct
    3130
  • src/ControlStruct/MultiLevelExit.cpp

    r92538ab r4559b34  
    99// Author           : Andrew Beach
    1010// Created On       : Mon Nov  1 13:48:00 2021
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Mar 28  9:42:00 2022
    13 // Update Count     : 34
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Wed Feb  2 23:07:54 2022
     13// Update Count     : 33
    1414//
    1515
     
    4040
    4141        enum Kind {
    42                 ForStmtK, WhileDoStmtK, CompoundStmtK, IfStmtK, CaseClauseK, SwitchStmtK, TryStmtK
     42                ForStmtK, WhileDoStmtK, CompoundStmtK, IfStmtK, CaseStmtK, SwitchStmtK, TryStmtK
    4343        } kind;
    4444
     
    5858        Entry( const IfStmt *stmt, Label breakExit ) :
    5959                stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( IfStmtK ) {}
    60         Entry( const CaseClause *, const CompoundStmt *stmt, Label fallExit ) :
    61                 stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseClauseK ) {}
     60        Entry( const CaseStmt *stmt, Label fallExit ) :
     61                stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseStmtK ) {}
    6262        Entry( const SwitchStmt *stmt, Label breakExit, Label fallDefaultExit ) :
    6363                stmt( stmt ), firstTarget( breakExit ), secondTarget( fallDefaultExit ), kind( SwitchStmtK ) {}
     
    6666
    6767        bool isContTarget() const { return kind <= WhileDoStmtK; }
    68         bool isBreakTarget() const { return kind != CaseClauseK; }
    69         bool isFallTarget() const { return kind == CaseClauseK; }
     68        bool isBreakTarget() const { return kind != CaseStmtK; }
     69        bool isFallTarget() const { return kind == CaseStmtK; }
    7070        bool isFallDefaultTarget() const { return kind == SwitchStmtK; }
    7171
    7272        // These routines set a target as being "used" by a BranchStmt
    7373        Label useContExit() { assert( kind <= WhileDoStmtK ); return useTarget(secondTarget); }
    74         Label useBreakExit() { assert( kind != CaseClauseK ); return useTarget(firstTarget); }
    75         Label useFallExit() { assert( kind == CaseClauseK );  return useTarget(firstTarget); }
     74        Label useBreakExit() { assert( kind != CaseStmtK ); return useTarget(firstTarget); }
     75        Label useFallExit() { assert( kind == CaseStmtK );  return useTarget(firstTarget); }
    7676        Label useFallDefaultExit() { assert( kind == SwitchStmtK ); return useTarget(secondTarget); }
    7777
    7878        // These routines check if a specific label for a statement is used by a BranchStmt
    7979        bool isContUsed() const { assert( kind <= WhileDoStmtK ); return secondTarget.used; }
    80         bool isBreakUsed() const { assert( kind != CaseClauseK ); return firstTarget.used; }
    81         bool isFallUsed() const { assert( kind == CaseClauseK ); return firstTarget.used; }
     80        bool isBreakUsed() const { assert( kind != CaseStmtK ); return firstTarget.used; }
     81        bool isFallUsed() const { assert( kind == CaseStmtK ); return firstTarget.used; }
    8282        bool isFallDefaultUsed() const { assert( kind == SwitchStmtK ); return secondTarget.used; }
    8383        void seenDefault() { fallDefaultValid = false; }
     
    115115        void previsit( const ForStmt * );
    116116        const ForStmt * postvisit( const ForStmt * );
    117         const CaseClause * previsit( const CaseClause * );
     117        const CaseStmt * previsit( const CaseStmt * );
    118118        void previsit( const IfStmt * );
    119119        const IfStmt * postvisit( const IfStmt * );
     
    123123        void previsit( const TryStmt * );
    124124        void postvisit( const TryStmt * );
    125         void previsit( const FinallyClause * );
     125        void previsit( const FinallyStmt * );
    126126
    127127        const Stmt * mutateLoop( const Stmt * body, Entry& );
     
    288288                  auto switchStmt = strict_dynamic_cast< const SwitchStmt * >( targetEntry->stmt );
    289289                  bool foundDefault = false;
    290                   for ( auto caseStmt : switchStmt->cases ) {
     290                  for ( auto subStmt : switchStmt->stmts ) {
     291                          const CaseStmt * caseStmt = subStmt.strict_as<CaseStmt>();
    291292                          if ( caseStmt->isDefault() ) {
    292293                                  foundDefault = true;
     
    364365}
    365366
    366 const CaseClause * MultiLevelExitCore::previsit( const CaseClause * stmt ) {
     367const CaseStmt * MultiLevelExitCore::previsit( const CaseStmt * stmt ) {
    367368        visit_children = false;
    368369
     
    374375
    375376        // The cond may not exist, but if it does update it now.
    376         visitor->maybe_accept( stmt, &CaseClause::cond );
     377        visitor->maybe_accept( stmt, &CaseStmt::cond );
    377378
    378379        // Just save the mutated node for simplicity.
    379         CaseClause * mutStmt = mutate( stmt );
    380 
    381         Label fallLabel = newLabel( "fallThrough", stmt->location );
     380        CaseStmt * mutStmt = mutate( stmt );
     381
     382        Label fallLabel = newLabel( "fallThrough", stmt );
    382383        if ( ! mutStmt->stmts.empty() ) {
    383                 // These should already be in a block.
    384                 auto first = mutStmt->stmts.front().get_and_mutate();
    385                 auto block = strict_dynamic_cast<CompoundStmt *>( first );
    386 
    387384                // Ensure that the stack isn't corrupted by exceptions in fixBlock.
    388385                auto guard = makeFuncGuard(
    389                         [&](){ enclosing_control_structures.emplace_back( mutStmt, block, fallLabel ); },
     386                        [&](){ enclosing_control_structures.emplace_back( mutStmt, fallLabel ); },
    390387                        [this](){ enclosing_control_structures.pop_back(); }
    391388                        );
    392389
     390                // These should already be in a block.
     391                auto block = mutate( mutStmt->stmts.front().strict_as<CompoundStmt>() );
    393392                block->kids = fixBlock( block->kids, true );
    394393
     
    397396                Entry & entry = enclosing_control_structures.back();
    398397                if ( entry.isFallUsed() ) {
    399                         mutStmt->stmts.push_back( labelledNullStmt( block->location, entry.useFallExit() ) );
     398                        mutStmt->stmts.push_back( labelledNullStmt( mutStmt->location, entry.useFallExit() ) );
    400399                }
    401400        }
     
    434433}
    435434
    436 static bool isDefaultCase( const ptr<CaseClause> & caseClause ) {
    437         return caseClause->isDefault();
     435bool isDefaultCase( const ptr<Stmt> & stmt ) {
     436        const CaseStmt * caseStmt = stmt.strict_as<CaseStmt>();
     437        return caseStmt->isDefault();
    438438}
    439439
    440440void MultiLevelExitCore::previsit( const SwitchStmt * stmt ) {
    441441        Label label = newLabel( "switchBreak", stmt );
    442         auto it = find_if( stmt->cases.rbegin(), stmt->cases.rend(), isDefaultCase );
    443 
    444         const CaseClause * defaultCase = it != stmt->cases.rend() ? (*it) : nullptr;
    445         Label defaultLabel = defaultCase ? newLabel( "fallThroughDefault", defaultCase->location ) : Label( stmt->location, "" );
     442        auto it = find_if( stmt->stmts.rbegin(), stmt->stmts.rend(), isDefaultCase );
     443
     444        const CaseStmt * defaultCase = it != stmt->stmts.rend() ? (it)->strict_as<CaseStmt>() : nullptr;
     445        Label defaultLabel = defaultCase ? newLabel( "fallThroughDefault", defaultCase ) : Label( stmt->location, "" );
    446446        enclosing_control_structures.emplace_back( stmt, label, defaultLabel );
    447447        GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
     
    449449        // Collect valid labels for fallthrough. It starts with all labels at this level, then remove as each is seen during
    450450        // traversal.
    451         for ( const CaseClause * caseStmt : stmt->cases ) {
     451        for ( const Stmt * stmt : stmt->stmts ) {
     452                auto * caseStmt = strict_dynamic_cast< const CaseStmt * >( stmt );
    452453                if ( caseStmt->stmts.empty() ) continue;
    453454                auto block = caseStmt->stmts.front().strict_as<CompoundStmt>();
     
    470471                // exit label and break to the last case, create a default case if no cases.
    471472                SwitchStmt * mutStmt = mutate( stmt );
    472                 if ( mutStmt->cases.empty() ) {
    473                         mutStmt->cases.push_back( new CaseClause( mutStmt->location, nullptr, {} ) );
    474                 }
    475 
    476                 auto caseStmt = mutStmt->cases.back().get();
     473                if ( mutStmt->stmts.empty() ) {
     474                        mutStmt->stmts.push_back( new CaseStmt( mutStmt->location, nullptr, {} ) );
     475                }
     476
     477                auto caseStmt = mutStmt->stmts.back().strict_as<CaseStmt>();
    477478                auto mutCase = mutate( caseStmt );
    478                 mutStmt->cases.back() = mutCase;
     479                mutStmt->stmts.back() = mutCase;
    479480
    480481                Label label( mutCase->location, "breakLabel" );
     
    513514}
    514515
    515 void MultiLevelExitCore::previsit( const FinallyClause * ) {
     516void MultiLevelExitCore::previsit( const FinallyStmt * ) {
    516517        GuardAction([this, old = move( enclosing_control_structures)](){ enclosing_control_structures = move(old); });
    517518        enclosing_control_structures = vector<Entry>();
  • src/InitTweak/FixGlobalInit.cc

    r92538ab r4559b34  
    113113                accept_all(translationUnit, fixer);
    114114
    115                 // Say these magic declarations come at the end of the file.
    116                 CodeLocation const & location = translationUnit.decls.back()->location;
    117 
    118115                if ( !fixer.core.initStmts.empty() ) {
    119116                        std::vector<ast::ptr<ast::Expr>> ctorParams;
    120                         if (inLibrary) ctorParams.emplace_back(ast::ConstantExpr::from_int(location, 200));
    121                         auto initFunction = new ast::FunctionDecl(location,
    122                                 "__global_init__", {}, {}, {},
    123                                 new ast::CompoundStmt(location, std::move(fixer.core.initStmts)),
    124                                 ast::Storage::Static, ast::Linkage::C,
    125                                 {new ast::Attribute("constructor", std::move(ctorParams))});
     117                        if (inLibrary) ctorParams.emplace_back(ast::ConstantExpr::from_int({}, 200));
     118                        auto initFunction = new ast::FunctionDecl({}, "__global_init__", {}, {}, {}, new ast::CompoundStmt({}, std::move(fixer.core.initStmts)),
     119                                ast::Storage::Static, ast::Linkage::C, {new ast::Attribute("constructor", std::move(ctorParams))});
    126120
    127121                        translationUnit.decls.emplace_back( initFunction );
     
    130124                if ( !fixer.core.destroyStmts.empty() ) {
    131125                        std::vector<ast::ptr<ast::Expr>> dtorParams;
    132                         if (inLibrary) dtorParams.emplace_back(ast::ConstantExpr::from_int(location, 200));
    133                         auto destroyFunction = new ast::FunctionDecl( location,
    134                                 "__global_destroy__", {}, {}, {},
    135                                 new ast::CompoundStmt(location, std::move(fixer.core.destroyStmts)),
    136                                 ast::Storage::Static, ast::Linkage::C,
    137                                 {new ast::Attribute("destructor", std::move(dtorParams))});
     126                        if (inLibrary) dtorParams.emplace_back(ast::ConstantExpr::from_int({}, 200));
     127                        auto destroyFunction = new ast::FunctionDecl({}, "__global_destroy__", {}, {}, {}, new ast::CompoundStmt({}, std::move(fixer.core.destroyStmts)),
     128                                ast::Storage::Static, ast::Linkage::C, {new ast::Attribute("destructor", std::move(dtorParams))});
    138129
    139130                        translationUnit.decls.emplace_back(destroyFunction);
  • src/InitTweak/FixInitNew.cpp

    r92538ab r4559b34  
    1616#include "CodeGen/GenType.h"           // for genPrettyType
    1717#include "CodeGen/OperatorTable.h"
    18 #include "Common/CodeLocationTools.hpp"
    1918#include "Common/PassVisitor.h"        // for PassVisitor, WithStmtsToAdd
    2019#include "Common/SemanticError.h"      // for SemanticError
     
    8685        /// generate/resolve copy construction expressions for each, and generate/resolve destructors for both
    8786        /// arguments and return value temporaries
    88         struct ResolveCopyCtors final : public ast::WithGuards, public ast::WithStmtsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithVisitorRef<ResolveCopyCtors>, public ast::WithConstTranslationUnit {
     87        struct ResolveCopyCtors final : public ast::WithGuards, public ast::WithStmtsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithVisitorRef<ResolveCopyCtors> {
    8988                const ast::Expr * postvisit( const ast::ImplicitCopyCtorExpr * impCpCtorExpr );
    9089                const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
     
    190189        /// for any member that is missing a corresponding ctor/dtor call.
    191190        /// error if a member is used before constructed
    192         struct GenStructMemberCalls final : public ast::WithGuards, public ast::WithShortCircuiting, public ast::WithSymbolTable, public ast::WithVisitorRef<GenStructMemberCalls>, public ast::WithConstTranslationUnit {
     191        struct GenStructMemberCalls final : public ast::WithGuards, public ast::WithShortCircuiting, public ast::WithSymbolTable, public ast::WithVisitorRef<GenStructMemberCalls> {
    193192                void previsit( const ast::FunctionDecl * funcDecl );
    194193                const ast::DeclWithType * postvisit( const ast::FunctionDecl * funcDecl );
     
    215214
    216215        /// expands ConstructorExpr nodes into comma expressions, using a temporary for the first argument
    217         struct FixCtorExprs final : public ast::WithDeclsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithConstTranslationUnit {
     216        struct FixCtorExprs final : public ast::WithDeclsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting {
    218217                const ast::Expr * postvisit( const ast::ConstructorExpr * ctorExpr );
    219218        };
     
    510509                // (VariableExpr and already resolved expression)
    511510                CP_CTOR_PRINT( std::cerr << "ResolvingCtorDtor " << untyped << std::endl; )
    512                 ast::ptr<ast::Expr> resolved = ResolvExpr::findVoidExpression(untyped, { symtab, transUnit().global } );
     511                ast::ptr<ast::Expr> resolved = ResolvExpr::findVoidExpression(untyped, symtab);
    513512                assert( resolved );
    514513                if ( resolved->env ) {
     
    554553                ast::ptr<ast::Expr> guard = mutArg;
    555554
    556                 ast::ptr<ast::ObjectDecl> tmp = new ast::ObjectDecl(loc, "__tmp", mutResult, nullptr );
     555                ast::ptr<ast::ObjectDecl> tmp = new ast::ObjectDecl({}, "__tmp", mutResult, nullptr );
    557556
    558557                // create and resolve copy constructor
     
    588587
    589588        ast::Expr * ResolveCopyCtors::destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg ) {
    590                 auto global = transUnit().global;
    591589                // TODO: refactor code for generating cleanup attribute, since it's common and reused in ~3-4 places
    592590                // check for existing cleanup attribute before adding another(?)
    593591                // need to add __Destructor for _tmp_cp variables as well
    594592
    595                 assertf( global.dtorStruct, "Destructor generation requires __Destructor definition." );
    596                 assertf( global.dtorStruct->members.size() == 2, "__Destructor definition does not have expected fields." );
    597                 assertf( global.dtorDestroy, "Destructor generation requires __destroy_Destructor." );
     593                assertf( ast::dtorStruct, "Destructor generation requires __Destructor definition." );
     594                assertf( ast::dtorStruct->members.size() == 2, "__Destructor definition does not have expected fields." );
     595                assertf( ast::dtorStructDestroy, "Destructor generation requires __destroy_Destructor." );
    598596
    599597                const CodeLocation loc = ret->location;
     
    612610                auto dtorFunc = getDtorFunc( ret, new ast::ExprStmt(loc, dtor ), stmtsToAddBefore );
    613611
    614                 auto dtorStructType = new ast::StructInstType( global.dtorStruct );
     612                auto dtorStructType = new ast::StructInstType(ast::dtorStruct);
    615613
    616614                // what does this do???
     
    624622                static UniqueName namer( "_ret_dtor" );
    625623                auto retDtor = new ast::ObjectDecl(loc, namer.newName(), dtorStructType, new ast::ListInit(loc, { new ast::SingleInit(loc, ast::ConstantExpr::null(loc) ), new ast::SingleInit(loc, new ast::CastExpr( new ast::VariableExpr(loc, dtorFunc ), dtorType ) ) } ) );
    626                 retDtor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, global.dtorDestroy ) } ) );
     624                retDtor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, ast::dtorStructDestroy ) } ) );
    627625                stmtsToAddBefore.push_back( new ast::DeclStmt(loc, retDtor ) );
    628626
    629627                if ( arg ) {
    630                         auto member = new ast::MemberExpr(loc, global.dtorStruct->members.front().strict_as<ast::DeclWithType>(), new ast::VariableExpr(loc, retDtor ) );
     628                        auto member = new ast::MemberExpr(loc, ast::dtorStruct->members.front().strict_as<ast::DeclWithType>(), new ast::VariableExpr(loc, retDtor ) );
    631629                        auto object = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, ret ) ), new ast::PointerType(new ast::VoidType() ) );
    632630                        ast::Expr * assign = createBitwiseAssignment( member, object );
     
    801799        // to prevent warnings ('_unq0' may be used uninitialized in this function),
    802800        // insert an appropriate zero initializer for UniqueExpr temporaries.
    803         ast::Init * makeInit( const ast::Type * t, CodeLocation const & loc ) {
     801        ast::Init * makeInit( const ast::Type * t ) {
    804802                if ( auto inst = dynamic_cast< const ast::StructInstType * >( t ) ) {
    805803                        // initizer for empty struct must be empty
    806                         if ( inst->base->members.empty() ) {
    807                                 return new ast::ListInit( loc, {} );
    808                         }
     804                        if ( inst->base->members.empty() ) return new ast::ListInit({}, {});
    809805                } else if ( auto inst = dynamic_cast< const ast::UnionInstType * >( t ) ) {
    810806                        // initizer for empty union must be empty
    811                         if ( inst->base->members.empty() ) {
    812                                 return new ast::ListInit( loc, {} );
    813                         }
    814                 }
    815 
    816                 return new ast::ListInit( loc, {
    817                         new ast::SingleInit( loc, ast::ConstantExpr::from_int( loc, 0 ) )
    818                 } );
     807                        if ( inst->base->members.empty() ) return new ast::ListInit({}, {});
     808                }
     809
     810                return new ast::ListInit( {}, { new ast::SingleInit( {}, ast::ConstantExpr::from_int({}, 0) ) } );
    819811        }
    820812
     
    840832                        } else {
    841833                                // expr isn't a call expr, so create a new temporary variable to use to hold the value of the unique expression
    842                                 mutExpr->object = new ast::ObjectDecl( mutExpr->location, toString("_unq", mutExpr->id), mutExpr->result, makeInit( mutExpr->result, mutExpr->location ) );
     834                                mutExpr->object = new ast::ObjectDecl( mutExpr->location, toString("_unq", mutExpr->id), mutExpr->result, makeInit( mutExpr->result ) );
    843835                                mutExpr->var = new ast::VariableExpr( mutExpr->location, mutExpr->object );
    844836                        }
     
    11801172                        auto guard = makeFuncGuard( [this]() { symtab.enterScope(); }, [this]() { symtab.leaveScope(); } );
    11811173                        symtab.addFunction( function );
    1182                         auto global = transUnit().global;
    11831174
    11841175                        // need to iterate through members in reverse in order for
     
    12261217
    12271218                                                        static UniqueName memberDtorNamer = { "__memberDtor" };
    1228                                                         assertf( global.dtorStruct, "builtin __Destructor not found." );
    1229                                                         assertf( global.dtorDestroy, "builtin __destroy_Destructor not found." );
     1219                                                        assertf( ast::dtorStruct, "builtin __Destructor not found." );
     1220                                                        assertf( ast::dtorStructDestroy, "builtin __destroy_Destructor not found." );
    12301221
    12311222                                                        ast::Expr * thisExpr = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, thisParam ) ), new ast::PointerType( new ast::VoidType(), ast::CV::Qualifiers() ) );
     
    12371228                                                        auto dtorType = new ast::PointerType( dtorFtype );
    12381229
    1239                                                         auto destructor = new ast::ObjectDecl(loc, memberDtorNamer.newName(), new ast::StructInstType( global.dtorStruct ), new ast::ListInit(loc, { new ast::SingleInit(loc, thisExpr ), new ast::SingleInit(loc, new ast::CastExpr( dtorExpr, dtorType ) ) } ) );
    1240                                                         destructor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr( loc, global.dtorDestroy ) } ) );
     1230                                                        auto destructor = new ast::ObjectDecl(loc, memberDtorNamer.newName(), new ast::StructInstType( ast::dtorStruct ), new ast::ListInit(loc, { new ast::SingleInit(loc, thisExpr ), new ast::SingleInit(loc, new ast::CastExpr( dtorExpr, dtorType ) ) } ) );
     1231                                                        destructor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr({}, ast::dtorStructDestroy ) } ) );
    12411232                                                        mutStmts->push_front( new ast::DeclStmt(loc, destructor ) );
    12421233                                                        mutStmts->kids.splice( mutStmts->kids.begin(), stmtsToAdd );
     
    13321323
    13331324        const ast::Expr * GenStructMemberCalls::postvisit( const ast::UntypedExpr * untypedExpr ) {
     1325                // Expression * newExpr = untypedExpr;
    13341326                // xxx - functions returning ast::ptr seems wrong...
    1335                 auto res = ResolvExpr::findVoidExpression( untypedExpr, { symtab, transUnit().global } );
    1336                 // Fix CodeLocation (at least until resolver is fixed).
    1337                 auto fix = localFillCodeLocations( untypedExpr->location, res.release() );
    1338                 return strict_dynamic_cast<const ast::Expr *>( fix );
     1327                auto res = ResolvExpr::findVoidExpression( untypedExpr, symtab );
     1328                return res.release();
     1329                // return newExpr;
    13391330        }
    13401331
     
    13701361
    13711362                // resolve assignment and dispose of new env
    1372                 auto resolved = ResolvExpr::findVoidExpression( assign, { symtab, transUnit().global } );
     1363                auto resolved = ResolvExpr::findVoidExpression( assign, symtab );
    13731364                auto mut = resolved.get_and_mutate();
    13741365                assertf(resolved.get() == mut, "newly resolved expression must be unique");
  • src/InitTweak/InitTweak.cc

    r92538ab r4559b34  
    423423                                loc, targetLabel.newName(), { new ast::Attribute{ "unused" } } };
    424424
    425                         std::vector< ast::ptr< ast::CaseClause > > branches;
     425                        std::vector< ast::ptr< ast::Stmt > > branches;
    426426                        for ( const ast::Init * init : *listInit ) {
    427427                                auto condition = ast::ConstantExpr::from_ulong( loc, cond );
     
    432432                                stmts.emplace_back(
    433433                                        new ast::BranchStmt{ loc, ast::BranchStmt::Break, switchLabel } );
    434                                 branches.emplace_back( new ast::CaseClause{ loc, condition, std::move( stmts ) } );
     434                                branches.emplace_back( new ast::CaseStmt{ loc, condition, std::move( stmts ) } );
    435435                        }
    436436                        out.emplace_back( new ast::SwitchStmt{ loc, index, std::move( branches ) } );
  • src/Parser/parser.yy

    r92538ab r4559b34  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Mar 14 16:35:29 2022
    13 // Update Count     : 5276
     12// Last Modified On : Tue Feb  1 11:06:13 2022
     13// Update Count     : 5167
    1414//
    1515
     
    610610        // | RESUME '(' comma_expression ')' compound_statement
    611611        //      { SemanticError( yylloc, "Resume expression is currently unimplemented." ); $$ = nullptr; }
    612         | IDENTIFIER IDENTIFIER                                                         // syntax error
    613                 {
    614                         SemanticError( yylloc, ::toString( "Adjacent identifiers are not meaningful in an expression. "
    615                                                                                            "Possible problem is identifier \"", *$1.str,
    616                                                                                            "\" is a misspelled typename or an incorrectly specified type name, "
    617                                                                                            "e.g., missing generic parameter or missing struct/union/enum before typename." ) );
    618                         $$ = nullptr;
    619                 }
    620         | IDENTIFIER direct_type                                                        // syntax error
    621                 {
    622                         SemanticError( yylloc, ::toString( "Identifier \"", *$1.str, "\" cannot appear before a type. "
    623                                                                                            "Possible problem is misspelled storage or CV qualifier." ) );
    624                         $$ = nullptr;
    625                 }
    626612        ;
    627613
     
    652638                        // Historic, transitional: Disallow commas in subscripts.
    653639                        // Switching to this behaviour may help check if a C compatibilty case uses comma-exprs in subscripts.
     640                // { SemanticError( yylloc, "New array subscript is currently unimplemented." ); $$ = nullptr; }
    654641                        // Current: Commas in subscripts make tuples.
    655642                { $$ = new ExpressionNode( build_binary_val( OperKinds::Index, $1, new ExpressionNode( build_tuple( (ExpressionNode *)($3->set_last( $5 ) ) )) ) ); }
     
    660647                // equivalent to the old x[i,j].
    661648                { $$ = new ExpressionNode( build_binary_val( OperKinds::Index, $1, $3 ) ); }
    662         | constant '[' assignment_expression ']'                        // 3[a], 'a'[a], 3.5[a]
    663                 { $$ = new ExpressionNode( build_binary_val( OperKinds::Index, $1, $3 ) ); }
    664         | string_literal '[' assignment_expression ']'          // "abc"[3], 3["abc"]
    665                 { $$ = new ExpressionNode( build_binary_val( OperKinds::Index, new ExpressionNode( $1 ), $3 ) ); }
    666649        | postfix_expression '{' argument_expression_list_opt '}' // CFA, constructor call
    667650                {
     
    10691052        identifier_or_type_name ':' attribute_list_opt statement
    10701053                { $$ = $4->add_label( $1, $3 ); }
    1071         | identifier_or_type_name ':' attribute_list_opt error // syntax error
    1072                 {
    1073                         SemanticError( yylloc, ::toString( "Label \"", *$1.str, "\" must be associated with a statement, "
    1074                                                                                            "where a declaration, case, or default is not a statement. "
    1075                                                                                            "Move the label or terminate with a semi-colon." ) );
    1076                         $$ = nullptr;
    1077                 }
    10781054        ;
    10791055
     
    11101086        | statement_list_nodecl statement
    11111087                { assert( $1 ); $1->set_last( $2 ); $$ = $1; }
    1112         | statement_list_nodecl error                                           // syntax error
    1113                 { SemanticError( yylloc, "Declarations only allowed at the start of the switch body, i.e., after the '{'." ); $$ = nullptr; }
    11141088        ;
    11151089
     
    11191093        | MUTEX '(' ')' comma_expression ';'
    11201094                { $$ = new StatementNode( build_mutex( nullptr, new StatementNode( build_expr( $4 ) ) ) ); }
     1095                // { SemanticError( yylloc, "Mutex expression is currently unimplemented." ); $$ = nullptr; }
    11211096        ;
    11221097
     
    11381113                        $$ = $7 ? new StatementNode( build_compound( (StatementNode *)((new StatementNode( $7 ))->set_last( sw )) ) ) : sw;
    11391114                }
    1140         | SWITCH '(' comma_expression ')' '{' error '}'         // CFA, syntax error
    1141                 { SemanticError( yylloc, "Only declarations can appear before the list of case clauses." ); $$ = nullptr; }
    11421115        | CHOOSE '(' comma_expression ')' case_clause           // CFA
    11431116                { $$ = new StatementNode( build_switch( false, $3, $5 ) ); }
     
    11471120                        $$ = $7 ? new StatementNode( build_compound( (StatementNode *)((new StatementNode( $7 ))->set_last( sw )) ) ) : sw;
    11481121                }
    1149         | CHOOSE '(' comma_expression ')' '{' error '}'         // CFA, syntax error
    1150                 { SemanticError( yylloc, "Only declarations can appear before the list of case clauses." ); $$ = nullptr; }
    11511122        ;
    11521123
     
    11871158
    11881159case_label:                                                                                             // CFA
    1189         CASE error                                                                                      // syntax error
    1190                 { SemanticError( yylloc, "Missing case list after case." ); $$ = nullptr; }
    1191         | CASE case_value_list ':'                                      { $$ = $2; }
    1192         | CASE case_value_list error                                            // syntax error
    1193                 { SemanticError( yylloc, "Missing colon after case list." ); $$ = nullptr; }
     1160        CASE case_value_list ':'                                        { $$ = $2; }
    11941161        | DEFAULT ':'                                                           { $$ = new StatementNode( build_default() ); }
    11951162                // A semantic check is required to ensure only one default clause per switch/choose statement.
    1196         | DEFAULT error                                                                         //  syntax error
    1197                 { SemanticError( yylloc, "Missing colon after default." ); $$ = nullptr; }
    1198         ;
     1163        ;
     1164
     1165//label_list_opt:
     1166//      // empty
     1167//      | identifier_or_type_name ':'
     1168//      | label_list_opt identifier_or_type_name ':'
     1169//      ;
    11991170
    12001171case_label_list:                                                                                // CFA
     
    12261197                { $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ) ) ); }
    12271198        | WHILE '(' conditional_declaration ')' statement ELSE statement // CFA
     1199                // { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
    12281200                { $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ), $7 ) ); }
    12291201        | DO statement WHILE '(' ')' ';'                                        // CFA => do while( 1 )
     
    12321204                { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
    12331205        | DO statement WHILE '(' comma_expression ')' ELSE statement // CFA
     1206                // { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
    12341207                { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ), $8 ) ); }
    12351208        | FOR '(' ')' statement                                                         // CFA => for ( ;; )
     
    12381211                { $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ) ) ); }
    12391212        | FOR '(' for_control_expression_list ')' statement ELSE statement // CFA
     1213                // { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
    12401214                { $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ), $7 ) ); }
    12411215        ;
     
    14321406        | when_clause_opt ELSE statement
    14331407                { $$ = build_waitfor_timeout( nullptr, maybe_build_compound( $3 ), $1 ); }
    1434         // "else" must be conditional after timeout or timeout is never triggered (i.e., it is meaningless)
    1435         | when_clause_opt timeout statement WOR ELSE statement // syntax error
     1408                // "else" must be conditional after timeout or timeout is never triggered (i.e., it is meaningless)
     1409        | when_clause_opt timeout statement WOR ELSE statement
    14361410                { SemanticError( yylloc, "else clause must be conditional after timeout or timeout never triggered." ); $$ = nullptr; }
    14371411        | when_clause_opt timeout statement WOR when_clause ELSE statement
     
    27612735        | ASM '(' string_literal ')' ';'                                        // GCC, global assembler statement
    27622736                { $$ = DeclarationNode::newAsmStmt( new StatementNode( build_asm( false, $3, 0 ) ) ); }
    2763         | EXTERN STRINGliteral
    2764                 {
    2765                         linkageStack.push( linkage );                           // handle nested extern "C"/"Cforall"
    2766                         linkage = LinkageSpec::update( yylloc, linkage, $2 );
    2767                 }
    2768           up external_definition down
    2769                 {
    2770                         linkage = linkageStack.top();
    2771                         linkageStack.pop();
    2772                         $$ = $5;
    2773                 }
    27742737        | EXTERN STRINGliteral                                                          // C++-style linkage specifier
    27752738                {
  • src/ResolvExpr/CandidateFinder.cpp

    r92538ab r4559b34  
    1010// Created On       : Wed Jun 5 14:30:00 2019
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar 16 11:58:00 2022
    13 // Update Count     : 3
     12// Last Modified On : Tue Oct  1 14:55:00 2019
     13// Update Count     : 2
    1414//
    1515
     
    595595        /// Actually visits expressions to find their candidate interpretations
    596596        class Finder final : public ast::WithShortCircuiting {
    597                 const ResolveContext & context;
    598597                const ast::SymbolTable & symtab;
    599598        public:
     
    619618
    620619                Finder( CandidateFinder & f )
    621                 : context( f.context ), symtab( context.symtab ), selfFinder( f ),
    622                   candidates( f.candidates ), tenv( f.env ), targetType( f.targetType ) {}
     620                : symtab( f.localSyms ), selfFinder( f ), candidates( f.candidates ), tenv( f.env ),
     621                  targetType( f.targetType ) {}
    623622
    624623                void previsit( const ast::Node * ) { visit_children = false; }
     
    873872                        Tuples::handleTupleAssignment( selfFinder, untypedExpr, argCandidates );
    874873
    875                         CandidateFinder funcFinder( context, tenv );
     874                        CandidateFinder funcFinder{ symtab, tenv };
    876875                        if (auto nameExpr = untypedExpr->func.as<ast::NameExpr>()) {
    877876                                auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
     
    919918                        // find function operators
    920919                        ast::ptr< ast::Expr > opExpr = new ast::NameExpr{ untypedExpr->location, "?()" };
    921                         CandidateFinder opFinder( context, tenv );
     920                        CandidateFinder opFinder{ symtab, tenv };
    922921                        // okay if there aren't any function operations
    923922                        opFinder.find( opExpr, ResolvMode::withoutFailFast() );
     
    10601059
    10611060                void postvisit( const ast::AddressExpr * addressExpr ) {
    1062                         CandidateFinder finder( context, tenv );
     1061                        CandidateFinder finder{ symtab, tenv };
    10631062                        finder.find( addressExpr->arg );
    10641063
     
    10801079                        ast::ptr< ast::Type > toType = castExpr->result;
    10811080                        assert( toType );
    1082                         toType = resolveTypeof( toType, context );
     1081                        toType = resolveTypeof( toType, symtab );
    10831082                        // toType = SymTab::validateType( castExpr->location, toType, symtab );
    10841083                        toType = adjustExprType( toType, tenv, symtab );
    10851084
    1086                         CandidateFinder finder( context, tenv, toType );
     1085                        CandidateFinder finder{ symtab, tenv, toType };
    10871086                        finder.find( castExpr->arg, ResolvMode::withAdjustment() );
    10881087
     
    11371136                void postvisit( const ast::VirtualCastExpr * castExpr ) {
    11381137                        assertf( castExpr->result, "Implicit virtual cast targets not yet supported." );
    1139                         CandidateFinder finder( context, tenv );
     1138                        CandidateFinder finder{ symtab, tenv };
    11401139                        // don't prune here, all alternatives guaranteed to have same type
    11411140                        finder.find( castExpr->arg, ResolvMode::withoutPrune() );
     
    11541153                        auto target = inst->base.get();
    11551154
    1156                         CandidateFinder finder( context, tenv );
     1155                        CandidateFinder finder{ symtab, tenv };
    11571156
    11581157                        auto pick_alternatives = [target, this](CandidateList & found, bool expect_ref) {
     
    12031202
    12041203                void postvisit( const ast::UntypedMemberExpr * memberExpr ) {
    1205                         CandidateFinder aggFinder( context, tenv );
     1204                        CandidateFinder aggFinder{ symtab, tenv };
    12061205                        aggFinder.find( memberExpr->aggregate, ResolvMode::withAdjustment() );
    12071206                        for ( CandidateRef & agg : aggFinder.candidates ) {
     
    12881287                                addCandidate(
    12891288                                        new ast::SizeofExpr{
    1290                                                 sizeofExpr->location, resolveTypeof( sizeofExpr->type, context ) },
     1289                                                sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) },
    12911290                                        tenv );
    12921291                        } else {
    12931292                                // find all candidates for the argument to sizeof
    1294                                 CandidateFinder finder( context, tenv );
     1293                                CandidateFinder finder{ symtab, tenv };
    12951294                                finder.find( sizeofExpr->expr );
    12961295                                // find the lowest-cost candidate, otherwise ambiguous
     
    13121311                                addCandidate(
    13131312                                        new ast::AlignofExpr{
    1314                                                 alignofExpr->location, resolveTypeof( alignofExpr->type, context ) },
     1313                                                alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) },
    13151314                                        tenv );
    13161315                        } else {
    13171316                                // find all candidates for the argument to alignof
    1318                                 CandidateFinder finder( context, tenv );
     1317                                CandidateFinder finder{ symtab, tenv };
    13191318                                finder.find( alignofExpr->expr );
    13201319                                // find the lowest-cost candidate, otherwise ambiguous
     
    13551354
    13561355                void postvisit( const ast::LogicalExpr * logicalExpr ) {
    1357                         CandidateFinder finder1( context, tenv );
     1356                        CandidateFinder finder1{ symtab, tenv };
    13581357                        finder1.find( logicalExpr->arg1, ResolvMode::withAdjustment() );
    13591358                        if ( finder1.candidates.empty() ) return;
    13601359
    1361                         CandidateFinder finder2( context, tenv );
     1360                        CandidateFinder finder2{ symtab, tenv };
    13621361                        finder2.find( logicalExpr->arg2, ResolvMode::withAdjustment() );
    13631362                        if ( finder2.candidates.empty() ) return;
     
    13851384                void postvisit( const ast::ConditionalExpr * conditionalExpr ) {
    13861385                        // candidates for condition
    1387                         CandidateFinder finder1( context, tenv );
     1386                        CandidateFinder finder1{ symtab, tenv };
    13881387                        finder1.find( conditionalExpr->arg1, ResolvMode::withAdjustment() );
    13891388                        if ( finder1.candidates.empty() ) return;
    13901389
    13911390                        // candidates for true result
    1392                         CandidateFinder finder2( context, tenv );
     1391                        CandidateFinder finder2{ symtab, tenv };
    13931392                        finder2.find( conditionalExpr->arg2, ResolvMode::withAdjustment() );
    13941393                        if ( finder2.candidates.empty() ) return;
    13951394
    13961395                        // candidates for false result
    1397                         CandidateFinder finder3( context, tenv );
     1396                        CandidateFinder finder3{ symtab, tenv };
    13981397                        finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() );
    13991398                        if ( finder3.candidates.empty() ) return;
     
    14461445                void postvisit( const ast::CommaExpr * commaExpr ) {
    14471446                        ast::TypeEnvironment env{ tenv };
    1448                         ast::ptr< ast::Expr > arg1 = resolveInVoidContext( commaExpr->arg1, context, env );
    1449 
    1450                         CandidateFinder finder2( context, env );
     1447                        ast::ptr< ast::Expr > arg1 = resolveInVoidContext( commaExpr->arg1, symtab, env );
     1448
     1449                        CandidateFinder finder2{ symtab, env };
    14511450                        finder2.find( commaExpr->arg2, ResolvMode::withAdjustment() );
    14521451
     
    14611460
    14621461                void postvisit( const ast::ConstructorExpr * ctorExpr ) {
    1463                         CandidateFinder finder( context, tenv );
     1462                        CandidateFinder finder{ symtab, tenv };
    14641463                        finder.find( ctorExpr->callExpr, ResolvMode::withoutPrune() );
    14651464                        for ( CandidateRef & r : finder.candidates ) {
     
    14701469                void postvisit( const ast::RangeExpr * rangeExpr ) {
    14711470                        // resolve low and high, accept candidates where low and high types unify
    1472                         CandidateFinder finder1( context, tenv );
     1471                        CandidateFinder finder1{ symtab, tenv };
    14731472                        finder1.find( rangeExpr->low, ResolvMode::withAdjustment() );
    14741473                        if ( finder1.candidates.empty() ) return;
    14751474
    1476                         CandidateFinder finder2( context, tenv );
     1475                        CandidateFinder finder2{ symtab, tenv };
    14771476                        finder2.find( rangeExpr->high, ResolvMode::withAdjustment() );
    14781477                        if ( finder2.candidates.empty() ) return;
     
    15501549
    15511550                void postvisit( const ast::UniqueExpr * unqExpr ) {
    1552                         CandidateFinder finder( context, tenv );
     1551                        CandidateFinder finder{ symtab, tenv };
    15531552                        finder.find( unqExpr->expr, ResolvMode::withAdjustment() );
    15541553                        for ( CandidateRef & r : finder.candidates ) {
     
    15591558
    15601559                void postvisit( const ast::StmtExpr * stmtExpr ) {
    1561                         addCandidate( resolveStmtExpr( stmtExpr, context ), tenv );
     1560                        addCandidate( resolveStmtExpr( stmtExpr, symtab ), tenv );
    15621561                }
    15631562
     
    15711570                        for ( const ast::InitAlternative & initAlt : initExpr->initAlts ) {
    15721571                                // calculate target type
    1573                                 const ast::Type * toType = resolveTypeof( initAlt.type, context );
     1572                                const ast::Type * toType = resolveTypeof( initAlt.type, symtab );
    15741573                                // toType = SymTab::validateType( initExpr->location, toType, symtab );
    15751574                                toType = adjustExprType( toType, tenv, symtab );
     
    15771576                                // types are not bound to the initialization type, since return type variables are
    15781577                                // only open for the duration of resolving the UntypedExpr.
    1579                                 CandidateFinder finder( context, tenv, toType );
     1578                                CandidateFinder finder{ symtab, tenv, toType };
    15801579                                finder.find( initExpr->expr, ResolvMode::withAdjustment() );
    15811580                                for ( CandidateRef & cand : finder.candidates ) {
     
    16941693                }
    16951694                else {
    1696                         satisfyAssertions(candidate, context.symtab, satisfied, errors);
     1695                        satisfyAssertions(candidate, localSyms, satisfied, errors);
    16971696                        needRecomputeKey = true;
    16981697                }
     
    18561855                        r->expr = ast::mutate_field(
    18571856                                r->expr.get(), &ast::Expr::result,
    1858                                 adjustExprType( r->expr->result, r->env, context.symtab ) );
     1857                                adjustExprType( r->expr->result, r->env, localSyms ) );
    18591858                }
    18601859        }
     
    18741873
    18751874        for ( const auto & x : xs ) {
    1876                 out.emplace_back( context, env );
     1875                out.emplace_back( localSyms, env );
    18771876                out.back().find( x, ResolvMode::withAdjustment() );
    18781877
  • src/ResolvExpr/CandidateFinder.hpp

    r92538ab r4559b34  
    1010// Created On       : Wed Jun 5 14:30:00 2019
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar 16 15:22:00 2022
    13 // Update Count     : 3
     12// Last Modified On : Tue Oct  1  9:51:00 2019
     13// Update Count     : 2
    1414//
    1515
     
    2525namespace ResolvExpr {
    2626
    27 struct ResolveContext;
    28 
    2927/// Data to perform expression resolution
    3028struct CandidateFinder {
    3129        CandidateList candidates;          ///< List of candidate resolutions
    32         const ResolveContext & context;  ///< Information about where the canditates are being found.
     30        const ast::SymbolTable & localSyms;   ///< Symbol table to lookup candidates
    3331        const ast::TypeEnvironment & env;  ///< Substitutions performed in this resolution
    3432        ast::ptr< ast::Type > targetType;  ///< Target type for resolution
     
    3634
    3735        CandidateFinder(
    38                 const ResolveContext & context, const ast::TypeEnvironment & env,
     36                const ast::SymbolTable & syms, const ast::TypeEnvironment & env,
    3937                const ast::Type * tt = nullptr )
    40         : candidates(), context( context ), env( env ), targetType( tt ) {}
     38        : candidates(), localSyms( syms ), env( env ), targetType( tt ) {}
    4139
    4240        /// Fill candidates with feasible resolutions for `expr`
  • src/ResolvExpr/CandidatePrinter.cpp

    r92538ab r4559b34  
    1010// Created On       : Tue Nov  9  9:54:00 2021
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar 16 13:56:00 2022
    13 // Update Count     : 1
     12// Last Modified On : Tue Nov  9 15:47:00 2021
     13// Update Count     : 0
    1414//
    1515
     
    2222#include "AST/TranslationUnit.hpp"
    2323#include "ResolvExpr/CandidateFinder.hpp"
    24 #include "ResolvExpr/Resolver.h"
    2524
    2625#include <iostream>
     
    3029namespace {
    3130
    32 class CandidatePrintCore : public ast::WithSymbolTable,
    33                 public ast::WithConstTranslationUnit {
     31class CandidatePrintCore : public ast::WithSymbolTable {
    3432        std::ostream & os;
    3533public:
     
    3836        void postvisit( const ast::ExprStmt * stmt ) {
    3937                ast::TypeEnvironment env;
    40                 CandidateFinder finder( { symtab, transUnit().global }, env );
     38                CandidateFinder finder( symtab, env );
    4139                finder.find( stmt->expr, ResolvMode::withAdjustment() );
    4240                int count = 1;
  • src/ResolvExpr/RenameVars.h

    r92538ab r4559b34  
    3636        };
    3737        const ast::Type * renameTyVars( const ast::Type *, RenameMode mode = GEN_USAGE, bool reset = true );
     38       
    3839
    3940        /// resets internal state of renamer to avoid overflow
    4041        void resetTyVarRenaming();
     42
     43       
    4144} // namespace ResolvExpr
    4245
  • src/ResolvExpr/ResolveTypeof.cc

    r92538ab r4559b34  
    99// Author           : Richard C. Bilson
    1010// Created On       : Sun May 17 12:12:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar 16 16:09:00 2022
    13 // Update Count     : 4
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Tue May 19 16:49:04 2015
     13// Update Count     : 3
    1414//
    1515
     
    2222#include "AST/Node.hpp"
    2323#include "AST/Pass.hpp"
    24 #include "AST/TranslationUnit.hpp"
    2524#include "AST/Type.hpp"
    2625#include "AST/TypeEnvironment.hpp"
     
    120119namespace {
    121120        struct ResolveTypeof_new : public ast::WithShortCircuiting {
    122                 const ResolveContext & context;
    123 
    124                 ResolveTypeof_new( const ResolveContext & context ) :
    125                         context( context ) {}
     121                const ast::SymbolTable & localSymtab;
     122
     123                ResolveTypeof_new( const ast::SymbolTable & syms ) : localSymtab( syms ) {}
    126124
    127125                void previsit( const ast::TypeofType * ) { visit_children = false; }
     
    139137                                ast::TypeEnvironment dummy;
    140138                                ast::ptr< ast::Expr > newExpr =
    141                                         resolveInVoidContext( typeofType->expr, context, dummy );
     139                                        resolveInVoidContext( typeofType->expr, localSymtab, dummy );
    142140                                assert( newExpr->result && ! newExpr->result->isVoid() );
    143141                                newType = newExpr->result;
     
    163161} // anonymous namespace
    164162
    165 const ast::Type * resolveTypeof( const ast::Type * type , const ResolveContext & context ) {
    166         ast::Pass< ResolveTypeof_new > mutator( context );
     163const ast::Type * resolveTypeof( const ast::Type * type , const ast::SymbolTable & symtab ) {
     164        ast::Pass< ResolveTypeof_new > mutator{ symtab };
    167165        return type->accept( mutator );
    168166}
    169167
    170168struct FixArrayDimension {
    171         const ResolveContext & context;
    172         FixArrayDimension(const ResolveContext & context) : context( context ) {}
     169        // should not require a mutable symbol table - prevent pass template instantiation
     170        const ast::SymbolTable & _symtab;
     171        FixArrayDimension(const ast::SymbolTable & symtab): _symtab(symtab) {}
    173172
    174173        const ast::ArrayType * previsit (const ast::ArrayType * arrayType) {
    175174                if (!arrayType->dimension) return arrayType;
    176175                auto mutType = mutate(arrayType);
    177                 auto globalSizeType = context.global.sizeType;
    178                 ast::ptr<ast::Type> sizetype = globalSizeType ? globalSizeType : new ast::BasicType(ast::BasicType::LongUnsignedInt);
    179                 mutType->dimension = findSingleExpression(arrayType->dimension, sizetype, context );
     176                ast::ptr<ast::Type> sizetype = ast::sizeType ? ast::sizeType : new ast::BasicType(ast::BasicType::LongUnsignedInt);
     177                mutType->dimension = findSingleExpression(arrayType->dimension, sizetype, _symtab);
    180178
    181179                if (InitTweak::isConstExpr(mutType->dimension)) {
     
    189187};
    190188
    191 const ast::Type * fixArrayType( const ast::Type * type, const ResolveContext & context ) {
    192         ast::Pass<FixArrayDimension> visitor(context);
     189const ast::Type * fixArrayType( const ast::Type * type, const ast::SymbolTable & symtab) {
     190        ast::Pass<FixArrayDimension> visitor {symtab};
    193191        return type->accept(visitor);
    194192}
    195193
    196 const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & context ) {
    197         if (decl->isTypeFixed) {
    198                 return decl;
    199         }
    200 
    201         auto mutDecl = mutate(decl);
    202         {
    203                 auto resolvedType = resolveTypeof(decl->type, context);
    204                 resolvedType = fixArrayType(resolvedType, context);
     194const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ast::SymbolTable & symtab ) {
     195        if (!decl->isTypeFixed) {
     196                auto mutDecl = mutate(decl);
     197                auto resolvedType = resolveTypeof(decl->type, symtab);
     198                resolvedType = fixArrayType(resolvedType, symtab);
    205199                mutDecl->type = resolvedType;
    206         }
    207 
    208         // Do not mangle unnamed variables.
    209         if (!mutDecl->name.empty()) {
    210                 mutDecl->mangleName = Mangle::mangle(mutDecl);
    211         }
    212 
    213         mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
    214         mutDecl->isTypeFixed = true;
    215         return mutDecl;
     200
     201                // check variable length if object is an array.
     202                // xxx - should this be part of fixObjectType?
     203
     204                /*
     205                if (auto arrayType = dynamic_cast<const ast::ArrayType *>(resolvedType)) {
     206                        auto dimExpr = findSingleExpression(arrayType->dimension, ast::sizeType, symtab);
     207                        if (auto varexpr = arrayType->dimension.as<ast::VariableExpr>()) {// hoisted previously
     208                                if (InitTweak::isConstExpr(varexpr->var.strict_as<ast::ObjectDecl>()->init)) {
     209                                        auto mutType = mutate(arrayType);
     210                                        mutType->isVarLen = ast::LengthFlag::VariableLen;
     211                                        mutDecl->type = mutType;
     212                                }
     213                        }
     214                }
     215                */
     216
     217
     218                if (!mutDecl->name.empty())
     219                        mutDecl->mangleName = Mangle::mangle(mutDecl); // do not mangle unnamed variables
     220               
     221                mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
     222                mutDecl->isTypeFixed = true;
     223                return mutDecl;
     224        }
     225        return decl;
    216226}
    217227
  • src/ResolvExpr/ResolveTypeof.h

    r92538ab r4559b34  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // ResolveTypeof.h --
     7// ResolveTypeof.h -- 
    88//
    99// Author           : Richard C. Bilson
    1010// Created On       : Sun May 17 12:14:53 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar 16 11:33:00 2022
    13 // Update Count     : 4
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Sat Jul 22 09:38:35 2017
     13// Update Count     : 3
    1414//
    1515
     
    2222namespace ast {
    2323        class Type;
     24        class SymbolTable;
    2425        class ObjectDecl;
    2526}
    2627
    2728namespace ResolvExpr {
    28         struct ResolveContext;
    29 
    3029        Type *resolveTypeof( Type*, const SymTab::Indexer &indexer );
    31         const ast::Type * resolveTypeof( const ast::Type *, const ResolveContext & );
    32         const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & );
     30        const ast::Type * resolveTypeof( const ast::Type *, const ast::SymbolTable & );
     31        const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ast::SymbolTable & symtab );
    3332} // namespace ResolvExpr
    3433
  • src/ResolvExpr/Resolver.cc

    r92538ab r4559b34  
    99// Author           : Aaron B. Moss
    1010// Created On       : Sun May 17 12:17:01 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Mar 18 10:41:00 2022
    13 // Update Count     : 247
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Tue Feb  1 16:27:14 2022
     13// Update Count     : 245
    1414//
    1515
     
    997997                /// Calls the CandidateFinder and finds the single best candidate
    998998                CandidateRef findUnfinishedKindExpression(
    999                         const ast::Expr * untyped, const ResolveContext & context, const std::string & kind,
     999                        const ast::Expr * untyped, const ast::SymbolTable & symtab, const std::string & kind,
    10001000                        std::function<bool(const Candidate &)> pred = anyCandidate, ResolvMode mode = {}
    10011001                ) {
     
    10071007                        ++recursion_level;
    10081008                        ast::TypeEnvironment env;
    1009                         CandidateFinder finder( context, env );
     1009                        CandidateFinder finder{ symtab, env };
    10101010                        finder.find( untyped, recursion_level == 1 ? mode.atTopLevel() : mode );
    10111011                        --recursion_level;
     
    11291129
    11301130        ast::ptr< ast::Expr > resolveInVoidContext(
    1131                 const ast::Expr * expr, const ResolveContext & context,
    1132                 ast::TypeEnvironment & env
     1131                const ast::Expr * expr, const ast::SymbolTable & symtab, ast::TypeEnvironment & env
    11331132        ) {
    11341133                assertf( expr, "expected a non-null expression" );
     
    11371136                ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr };
    11381137                CandidateRef choice = findUnfinishedKindExpression(
    1139                         untyped, context, "", anyCandidate, ResolvMode::withAdjustment() );
     1138                        untyped, symtab, "", anyCandidate, ResolvMode::withAdjustment() );
    11401139
    11411140                // a cast expression has either 0 or 1 interpretations (by language rules);
     
    11501149                /// context.
    11511150                ast::ptr< ast::Expr > findVoidExpression(
    1152                         const ast::Expr * untyped, const ResolveContext & context
     1151                        const ast::Expr * untyped, const ast::SymbolTable & symtab
    11531152                ) {
    11541153                        ast::TypeEnvironment env;
    1155                         ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, context, env );
     1154                        ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, symtab, env );
    11561155                        finishExpr( newExpr, env, untyped->env );
    11571156                        return newExpr;
     
    11641163                /// lowest cost, returning the resolved version
    11651164                ast::ptr< ast::Expr > findKindExpression(
    1166                         const ast::Expr * untyped, const ResolveContext & context,
     1165                        const ast::Expr * untyped, const ast::SymbolTable & symtab,
    11671166                        std::function<bool(const Candidate &)> pred = anyCandidate,
    11681167                        const std::string & kind = "", ResolvMode mode = {}
     
    11701169                        if ( ! untyped ) return {};
    11711170                        CandidateRef choice =
    1172                                 findUnfinishedKindExpression( untyped, context, kind, pred, mode );
     1171                                findUnfinishedKindExpression( untyped, symtab, kind, pred, mode );
    11731172                        ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env );
    11741173                        return std::move( choice->expr );
     
    11771176                /// Resolve `untyped` to the single expression whose candidate is the best match
    11781177                ast::ptr< ast::Expr > findSingleExpression(
    1179                         const ast::Expr * untyped, const ResolveContext & context
     1178                        const ast::Expr * untyped, const ast::SymbolTable & symtab
    11801179                ) {
    11811180                        Stats::ResolveTime::start( untyped );
    1182                         auto res = findKindExpression( untyped, context );
     1181                        auto res = findKindExpression( untyped, symtab );
    11831182                        Stats::ResolveTime::stop();
    11841183                        return res;
     
    11871186
    11881187        ast::ptr< ast::Expr > findSingleExpression(
    1189                 const ast::Expr * untyped, const ast::Type * type,
    1190                 const ResolveContext & context
     1188                const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab
    11911189        ) {
    11921190                assert( untyped && type );
    11931191                ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };
    1194                 ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, context );
    1195                 removeExtraneousCast( newExpr, context.symtab );
     1192                ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, symtab );
     1193                removeExtraneousCast( newExpr, symtab );
    11961194                return newExpr;
    11971195        }
     
    12191217                /// Resolve `untyped` as an integral expression, returning the resolved version
    12201218                ast::ptr< ast::Expr > findIntegralExpression(
    1221                         const ast::Expr * untyped, const ResolveContext & context
     1219                        const ast::Expr * untyped, const ast::SymbolTable & symtab
    12221220                ) {
    1223                         return findKindExpression( untyped, context, hasIntegralType, "condition" );
     1221                        return findKindExpression( untyped, symtab, hasIntegralType, "condition" );
    12241222                }
    12251223
     
    12511249                // for work previously in GenInit
    12521250                static InitTweak::ManagedTypes_new managedTypes;
    1253                 ResolveContext context;
    12541251
    12551252                bool inEnumDecl = false;
     
    12571254        public:
    12581255                static size_t traceId;
    1259                 Resolver_new( const ast::TranslationGlobal & global ) :
    1260                         context{ symtab, global } {}
    1261                 Resolver_new( const ResolveContext & context ) :
    1262                         ast::WithSymbolTable{ context.symtab },
    1263                         context{ symtab, context.global } {}
     1256                Resolver_new() = default;
     1257                Resolver_new( const ast::SymbolTable & syms ) { symtab = syms; }
    12641258
    12651259                const ast::FunctionDecl * previsit( const ast::FunctionDecl * );
     
    12781272                const ast::AsmStmt *         previsit( const ast::AsmStmt * );
    12791273                const ast::IfStmt *          previsit( const ast::IfStmt * );
    1280                 const ast::WhileDoStmt *     previsit( const ast::WhileDoStmt * );
     1274                const ast::WhileDoStmt *       previsit( const ast::WhileDoStmt * );
    12811275                const ast::ForStmt *         previsit( const ast::ForStmt * );
    12821276                const ast::SwitchStmt *      previsit( const ast::SwitchStmt * );
    1283                 const ast::CaseClause *      previsit( const ast::CaseClause * );
     1277                const ast::CaseStmt *        previsit( const ast::CaseStmt * );
    12841278                const ast::BranchStmt *      previsit( const ast::BranchStmt * );
    12851279                const ast::ReturnStmt *      previsit( const ast::ReturnStmt * );
    12861280                const ast::ThrowStmt *       previsit( const ast::ThrowStmt * );
    1287                 const ast::CatchClause *     previsit( const ast::CatchClause * );
    1288                 const ast::CatchClause *     postvisit( const ast::CatchClause * );
     1281                const ast::CatchStmt *       previsit( const ast::CatchStmt * );
     1282                const ast::CatchStmt *       postvisit( const ast::CatchStmt * );
    12891283                const ast::WaitForStmt *     previsit( const ast::WaitForStmt * );
    12901284                const ast::WithStmt *        previsit( const ast::WithStmt * );
     
    13051299
    13061300        void resolve( ast::TranslationUnit& translationUnit ) {
    1307                 ast::Pass< Resolver_new >::run( translationUnit, translationUnit.global );
     1301                ast::Pass< Resolver_new >::run( translationUnit );
    13081302        }
    13091303
    13101304        ast::ptr< ast::Init > resolveCtorInit(
    1311                 const ast::ConstructorInit * ctorInit, const ResolveContext & context
     1305                const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab
    13121306        ) {
    13131307                assert( ctorInit );
    1314                 ast::Pass< Resolver_new > resolver( context );
     1308                ast::Pass< Resolver_new > resolver{ symtab };
    13151309                return ctorInit->accept( resolver );
    13161310        }
    13171311
    13181312        const ast::Expr * resolveStmtExpr(
    1319                 const ast::StmtExpr * stmtExpr, const ResolveContext & context
     1313                const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab
    13201314        ) {
    13211315                assert( stmtExpr );
    1322                 ast::Pass< Resolver_new > resolver( context );
     1316                ast::Pass< Resolver_new > resolver{ symtab };
    13231317                auto ret = mutate(stmtExpr->accept(resolver));
    13241318                strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult();
     
    13271321
    13281322        namespace {
    1329                 const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ResolveContext & context) {
     1323                const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ast::SymbolTable & symtab) {
    13301324                        std::string name = attr->normalizedName();
    13311325                        if (name == "constructor" || name == "destructor") {
    13321326                                if (attr->params.size() == 1) {
    13331327                                        auto arg = attr->params.front();
    1334                                         auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicType::LongLongSignedInt ), context );
     1328                                        auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicType::LongLongSignedInt ), symtab );
    13351329                                        auto result = eval(arg);
    13361330
     
    13751369
    13761370                        for (auto & attr: mutDecl->attributes) {
    1377                                 attr = handleAttribute(mutDecl->location, attr, context );
     1371                                attr = handleAttribute(mutDecl->location, attr, symtab);
    13781372                        }
    13791373
     
    13851379                        for (auto & typeParam : mutDecl->type_params) {
    13861380                                symtab.addType(typeParam);
    1387                                 mutType->forall.emplace_back(new ast::TypeInstType(typeParam));
     1381                                mutType->forall.emplace_back(new ast::TypeInstType(typeParam->name, typeParam));
    13881382                        }
    13891383                        for (auto & asst : mutDecl->assertions) {
    1390                                 asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), context);
     1384                                asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab);
    13911385                                symtab.addId(asst);
    13921386                                mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));
     
    14001394
    14011395                        for (auto & param : mutDecl->params) {
    1402                                 param = fixObjectType(param.strict_as<ast::ObjectDecl>(), context);
     1396                                param = fixObjectType(param.strict_as<ast::ObjectDecl>(), symtab);
    14031397                                symtab.addId(param);
    14041398                                paramTypes.emplace_back(param->get_type());
    14051399                        }
    14061400                        for (auto & ret : mutDecl->returns) {
    1407                                 ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), context);
     1401                                ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), symtab);
    14081402                                returnTypes.emplace_back(ret->get_type());
    14091403                        }
     
    15041498                else {
    15051499                        if (!objectDecl->isTypeFixed) {
    1506                                 auto newDecl = fixObjectType(objectDecl, context);
     1500                                auto newDecl = fixObjectType(objectDecl, symtab);
    15071501                                auto mutDecl = mutate(newDecl);
    15081502
     
    15351529                        // nested type decls are hoisted already. no need to do anything
    15361530                        if (auto obj = member.as<ast::ObjectDecl>()) {
    1537                                 member = fixObjectType(obj, context);
     1531                                member = fixObjectType(obj, symtab);
    15381532                        }
    15391533                }
     
    15581552                return ast::mutate_field(
    15591553                        assertDecl, &ast::StaticAssertDecl::cond,
    1560                         findIntegralExpression( assertDecl->cond, context ) );
     1554                        findIntegralExpression( assertDecl->cond, symtab ) );
    15611555        }
    15621556
    15631557        template< typename PtrType >
    1564         const PtrType * handlePtrType( const PtrType * type, const ResolveContext & context ) {
     1558        const PtrType * handlePtrType( const PtrType * type, const ast::SymbolTable & symtab ) {
    15651559                if ( type->dimension ) {
    1566                         ast::ptr< ast::Type > sizeType = context.global.sizeType;
     1560                        ast::ptr< ast::Type > sizeType = ast::sizeType;
    15671561                        ast::mutate_field(
    15681562                                type, &PtrType::dimension,
    1569                                 findSingleExpression( type->dimension, sizeType, context ) );
     1563                                findSingleExpression( type->dimension, sizeType, symtab ) );
    15701564                }
    15711565                return type;
     
    15731567
    15741568        const ast::ArrayType * Resolver_new::previsit( const ast::ArrayType * at ) {
    1575                 return handlePtrType( at, context );
     1569                return handlePtrType( at, symtab );
    15761570        }
    15771571
    15781572        const ast::PointerType * Resolver_new::previsit( const ast::PointerType * pt ) {
    1579                 return handlePtrType( pt, context );
     1573                return handlePtrType( pt, symtab );
    15801574        }
    15811575
     
    15851579
    15861580                return ast::mutate_field(
    1587                         exprStmt, &ast::ExprStmt::expr, findVoidExpression( exprStmt->expr, context ) );
     1581                        exprStmt, &ast::ExprStmt::expr, findVoidExpression( exprStmt->expr, symtab ) );
    15881582        }
    15891583
     
    15921586
    15931587                asmExpr = ast::mutate_field(
    1594                         asmExpr, &ast::AsmExpr::operand, findVoidExpression( asmExpr->operand, context ) );
     1588                        asmExpr, &ast::AsmExpr::operand, findVoidExpression( asmExpr->operand, symtab ) );
    15951589
    15961590                return asmExpr;
     
    16061600        const ast::IfStmt * Resolver_new::previsit( const ast::IfStmt * ifStmt ) {
    16071601                return ast::mutate_field(
    1608                         ifStmt, &ast::IfStmt::cond, findIntegralExpression( ifStmt->cond, context ) );
     1602                        ifStmt, &ast::IfStmt::cond, findIntegralExpression( ifStmt->cond, symtab ) );
    16091603        }
    16101604
    16111605        const ast::WhileDoStmt * Resolver_new::previsit( const ast::WhileDoStmt * whileDoStmt ) {
    16121606                return ast::mutate_field(
    1613                         whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, context ) );
     1607                        whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, symtab ) );
    16141608        }
    16151609
     
    16171611                if ( forStmt->cond ) {
    16181612                        forStmt = ast::mutate_field(
    1619                                 forStmt, &ast::ForStmt::cond, findIntegralExpression( forStmt->cond, context ) );
     1613                                forStmt, &ast::ForStmt::cond, findIntegralExpression( forStmt->cond, symtab ) );
    16201614                }
    16211615
    16221616                if ( forStmt->inc ) {
    16231617                        forStmt = ast::mutate_field(
    1624                                 forStmt, &ast::ForStmt::inc, findVoidExpression( forStmt->inc, context ) );
     1618                                forStmt, &ast::ForStmt::inc, findVoidExpression( forStmt->inc, symtab ) );
    16251619                }
    16261620
     
    16321626                switchStmt = ast::mutate_field(
    16331627                        switchStmt, &ast::SwitchStmt::cond,
    1634                         findIntegralExpression( switchStmt->cond, context ) );
     1628                        findIntegralExpression( switchStmt->cond, symtab ) );
    16351629                currentObject = ast::CurrentObject{ switchStmt->location, switchStmt->cond->result };
    16361630                return switchStmt;
    16371631        }
    16381632
    1639         const ast::CaseClause * Resolver_new::previsit( const ast::CaseClause * caseStmt ) {
     1633        const ast::CaseStmt * Resolver_new::previsit( const ast::CaseStmt * caseStmt ) {
    16401634                if ( caseStmt->cond ) {
    16411635                        std::deque< ast::InitAlternative > initAlts = currentObject.getOptions();
     
    16451639                        ast::ptr< ast::Expr > untyped =
    16461640                                new ast::CastExpr{ caseStmt->location, caseStmt->cond, initAlts.front().type };
    1647                         ast::ptr< ast::Expr > newExpr = findSingleExpression( untyped, context );
     1641                        ast::ptr< ast::Expr > newExpr = findSingleExpression( untyped, symtab );
    16481642
    16491643                        // case condition cannot have a cast in C, so it must be removed here, regardless of
     
    16531647                        }
    16541648
    1655                         caseStmt = ast::mutate_field( caseStmt, &ast::CaseClause::cond, newExpr );
     1649                        caseStmt = ast::mutate_field( caseStmt, &ast::CaseStmt::cond, newExpr );
    16561650                }
    16571651                return caseStmt;
     
    16661660                        branchStmt = ast::mutate_field(
    16671661                                branchStmt, &ast::BranchStmt::computedTarget,
    1668                                 findSingleExpression( branchStmt->computedTarget, target, context ) );
     1662                                findSingleExpression( branchStmt->computedTarget, target, symtab ) );
    16691663                }
    16701664                return branchStmt;
     
    16761670                        returnStmt = ast::mutate_field(
    16771671                                returnStmt, &ast::ReturnStmt::expr,
    1678                                 findSingleExpression( returnStmt->expr, functionReturn, context ) );
     1672                                findSingleExpression( returnStmt->expr, functionReturn, symtab ) );
    16791673                }
    16801674                return returnStmt;
     
    16911685                        throwStmt = ast::mutate_field(
    16921686                                throwStmt, &ast::ThrowStmt::expr,
    1693                                 findSingleExpression( throwStmt->expr, exceptType, context ) );
     1687                                findSingleExpression( throwStmt->expr, exceptType, symtab ) );
    16941688                }
    16951689                return throwStmt;
    16961690        }
    16971691
    1698         const ast::CatchClause * Resolver_new::previsit( const ast::CatchClause * catchClause ) {
     1692        const ast::CatchStmt * Resolver_new::previsit( const ast::CatchStmt * catchStmt ) {
    16991693                // Until we are very sure this invarent (ifs that move between passes have then)
    17001694                // holds, check it. This allows a check for when to decode the mangling.
    1701                 if ( auto ifStmt = catchClause->body.as<ast::IfStmt>() ) {
     1695                if ( auto ifStmt = catchStmt->body.as<ast::IfStmt>() ) {
    17021696                        assert( ifStmt->then );
    17031697                }
    17041698                // Encode the catchStmt so the condition can see the declaration.
    1705                 if ( catchClause->cond ) {
    1706                         ast::CatchClause * clause = mutate( catchClause );
    1707                         clause->body = new ast::IfStmt( clause->location, clause->cond, nullptr, clause->body );
    1708                         clause->cond = nullptr;
    1709                         return clause;
    1710                 }
    1711                 return catchClause;
    1712         }
    1713 
    1714         const ast::CatchClause * Resolver_new::postvisit( const ast::CatchClause * catchClause ) {
     1699                if ( catchStmt->cond ) {
     1700                        ast::CatchStmt * stmt = mutate( catchStmt );
     1701                        stmt->body = new ast::IfStmt( stmt->location, stmt->cond, nullptr, stmt->body );
     1702                        stmt->cond = nullptr;
     1703                        return stmt;
     1704                }
     1705                return catchStmt;
     1706        }
     1707
     1708        const ast::CatchStmt * Resolver_new::postvisit( const ast::CatchStmt * catchStmt ) {
    17151709                // Decode the catchStmt so everything is stored properly.
    1716                 const ast::IfStmt * ifStmt = catchClause->body.as<ast::IfStmt>();
     1710                const ast::IfStmt * ifStmt = catchStmt->body.as<ast::IfStmt>();
    17171711                if ( nullptr != ifStmt && nullptr == ifStmt->then ) {
    17181712                        assert( ifStmt->cond );
    17191713                        assert( ifStmt->else_ );
    1720                         ast::CatchClause * clause = ast::mutate( catchClause );
    1721                         clause->cond = ifStmt->cond;
    1722                         clause->body = ifStmt->else_;
     1714                        ast::CatchStmt * stmt = ast::mutate( catchStmt );
     1715                        stmt->cond = ifStmt->cond;
     1716                        stmt->body = ifStmt->else_;
    17231717                        // ifStmt should be implicately deleted here.
    1724                         return clause;
    1725                 }
    1726                 return catchClause;
     1718                        return stmt;
     1719                }
     1720                return catchStmt;
    17271721        }
    17281722
     
    17351729
    17361730                        ast::TypeEnvironment env;
    1737                         CandidateFinder funcFinder( context, env );
     1731                        CandidateFinder funcFinder{ symtab, env };
    17381732
    17391733                        // Find all candidates for a function in canonical form
     
    19491943                                );
    19501944
    1951                                 clause2.target.args.emplace_back( findSingleExpression( init, context ) );
     1945                                clause2.target.args.emplace_back( findSingleExpression( init, symtab ) );
    19521946                        }
    19531947
    19541948                        // Resolve the conditions as if it were an IfStmt, statements normally
    1955                         clause2.cond = findSingleExpression( clause.cond, context );
     1949                        clause2.cond = findSingleExpression( clause.cond, symtab );
    19561950                        clause2.stmt = clause.stmt->accept( *visitor );
    19571951
     
    19681962                        ast::ptr< ast::Type > target =
    19691963                                new ast::BasicType{ ast::BasicType::LongLongUnsignedInt };
    1970                         timeout2.time = findSingleExpression( stmt->timeout.time, target, context );
    1971                         timeout2.cond = findSingleExpression( stmt->timeout.cond, context );
     1964                        timeout2.time = findSingleExpression( stmt->timeout.time, target, symtab );
     1965                        timeout2.cond = findSingleExpression( stmt->timeout.cond, symtab );
    19721966                        timeout2.stmt = stmt->timeout.stmt->accept( *visitor );
    19731967
     
    19821976                        ast::WaitForStmt::OrElse orElse2;
    19831977
    1984                         orElse2.cond = findSingleExpression( stmt->orElse.cond, context );
     1978                        orElse2.cond = findSingleExpression( stmt->orElse.cond, symtab );
    19851979                        orElse2.stmt = stmt->orElse.stmt->accept( *visitor );
    19861980
     
    20031997                for (auto & expr : exprs) {
    20041998                        // only struct- and union-typed expressions are viable candidates
    2005                         expr = findKindExpression( expr, context, structOrUnion, "with expression" );
     1999                        expr = findKindExpression( expr, symtab, structOrUnion, "with expression" );
    20062000
    20072001                        // if with expression might be impure, create a temporary so that it is evaluated once
     
    20292023                ast::ptr< ast::Expr > untyped = new ast::UntypedInitExpr{
    20302024                        singleInit->location, singleInit->value, currentObject.getOptions() };
    2031                 ast::ptr<ast::Expr> newExpr = findSingleExpression( untyped, context );
     2025                ast::ptr<ast::Expr> newExpr = findSingleExpression( untyped, symtab );
    20322026                const ast::InitExpr * initExpr = newExpr.strict_as< ast::InitExpr >();
    20332027
  • src/ResolvExpr/Resolver.h

    r92538ab r4559b34  
    99// Author           : Richard C. Bilson
    1010// Created On       : Sun May 17 12:18:34 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar 16 11:32:00 2022
    13 // Update Count     : 5
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Mon Feb 18 20:40:38 2019
     13// Update Count     : 4
    1414//
    1515
     
    2323class Declaration;
    2424class Expression;
    25 class DeletedExpr;
    2625class StmtExpr;
    27 class Type;
    2826namespace SymTab {
    2927        class Indexer;
     
    3735        class StmtExpr;
    3836        class SymbolTable;
    39         class TranslationGlobal;
    4037        class TranslationUnit;
    4138        class Type;
     
    5855        void resolveWithExprs( std::list< Declaration * > & translationUnit );
    5956
    60         /// Helper Type: Passes around information between various sub-calls.
    61         struct ResolveContext {
    62                 const ast::SymbolTable & symtab;
    63                 const ast::TranslationGlobal & global;
    64         };
    65 
    6657        /// Checks types and binds syntactic constructs to typed representations
    6758        void resolve( ast::TranslationUnit& translationUnit );
     
    7162        /// context.
    7263        ast::ptr< ast::Expr > resolveInVoidContext(
    73                 const ast::Expr * expr, const ResolveContext &, ast::TypeEnvironment & env );
     64                const ast::Expr * expr, const ast::SymbolTable & symtab, ast::TypeEnvironment & env );
    7465        /// Resolve `untyped` to the single expression whose candidate is the best match for the
    7566        /// given type.
    7667        ast::ptr< ast::Expr > findSingleExpression(
    77                 const ast::Expr * untyped, const ast::Type * type, const ResolveContext & );
     68                const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab );
    7869        ast::ptr< ast::Expr > findVoidExpression(
    79                 const ast::Expr * untyped, const ResolveContext & );
     70                const ast::Expr * untyped, const ast::SymbolTable & symtab);
    8071        /// Resolves a constructor init expression
    8172        ast::ptr< ast::Init > resolveCtorInit(
    82                 const ast::ConstructorInit * ctorInit, const ResolveContext & context );
     73                const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab );
    8374        /// Resolves a statement expression
    8475        const ast::Expr * resolveStmtExpr(
    85                 const ast::StmtExpr * stmtExpr, const ResolveContext & context );
     76                const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab );
    8677} // namespace ResolvExpr
    8778
  • src/ResolvExpr/Unify.cc

    r92538ab r4559b34  
    943943                        // check that the other type is compatible and named the same
    944944                        auto otherInst = dynamic_cast< const XInstType * >( other );
    945                         if (otherInst && inst->name == otherInst->name) this->result = otherInst;
     945                        this->result = otherInst && inst->name == otherInst->name;
    946946                        return otherInst;
    947947                }
  • src/SymTab/Validate.cc

    r92538ab r4559b34  
    194194        };
    195195
    196         // These structs are the sub-sub-passes of ForallPointerDecay_old.
    197 
    198         struct TraitExpander_old final {
    199                 void previsit( FunctionType * );
    200                 void previsit( StructDecl * );
    201                 void previsit( UnionDecl * );
    202         };
    203 
    204         struct AssertionFixer_old final {
    205                 void previsit( FunctionType * );
    206                 void previsit( StructDecl * );
    207                 void previsit( UnionDecl * );
    208         };
    209 
    210         struct CheckOperatorTypes_old final {
    211                 void previsit( ObjectDecl * );
    212         };
    213 
    214         struct FixUniqueIds_old final {
    215                 void previsit( DeclarationWithType * );
    216         };
    217 
    218196        struct ReturnChecker : public WithGuards {
    219197                /// Checks that return statements return nothing if their return type is void
     
    395373                                TranslateDimensionGenericParameters::translateDimensions( translationUnit );
    396374                        });
    397                         if (!useNewAST) {
    398375                        Stats::Time::TimeBlock("Resolve Enum Initializers", [&]() {
    399376                                acceptAll( translationUnit, rei ); // must happen after translateDimensions because rei needs identifier lookup, which needs name mangling
    400377                        });
    401                         }
    402378                        Stats::Time::TimeBlock("Check Function Returns", [&]() {
    403379                                ReturnChecker::checkFunctionReturns( translationUnit );
     
    409385        }
    410386
    411         static void decayForallPointers( std::list< Declaration * > & translationUnit ) {
    412                 PassVisitor<TraitExpander_old> te;
    413                 acceptAll( translationUnit, te );
    414                 PassVisitor<AssertionFixer_old> af;
    415                 acceptAll( translationUnit, af );
    416                 PassVisitor<CheckOperatorTypes_old> cot;
    417                 acceptAll( translationUnit, cot );
    418                 PassVisitor<FixUniqueIds_old> fui;
    419                 acceptAll( translationUnit, fui );
    420         }
    421 
    422387        void validate_D( std::list< Declaration * > & translationUnit ) {
     388                PassVisitor<ForallPointerDecay_old> fpd;
    423389                {
    424390                        Stats::Heap::newPass("validate-D");
     
    428394                        });
    429395                        Stats::Time::TimeBlock("Forall Pointer Decay", [&]() {
    430                                 decayForallPointers( translationUnit ); // must happen before autogenerateRoutines, after Concurrency::applyKeywords because uniqueIds must be set on declaration before resolution
     396                                acceptAll( translationUnit, fpd ); // must happen before autogenerateRoutines, after Concurrency::applyKeywords because uniqueIds must be set on declaration before resolution
    431397                        });
    432398                        Stats::Time::TimeBlock("Hoist Control Declarations", [&]() {
     
    487453        }
    488454
     455        void decayForallPointers( std::list< Declaration * > & translationUnit ) {
     456                PassVisitor<ForallPointerDecay_old> fpd;
     457                acceptAll( translationUnit, fpd );
     458        }
     459
    489460        void validate( std::list< Declaration * > &translationUnit, __attribute__((unused)) bool doDebug ) {
    490461                validate_A( translationUnit );
     
    499470                PassVisitor<EnumAndPointerDecay_old> epc;
    500471                PassVisitor<LinkReferenceToTypes_old> lrt( indexer );
    501                 PassVisitor<TraitExpander_old> te;
    502                 PassVisitor<AssertionFixer_old> af;
    503                 PassVisitor<CheckOperatorTypes_old> cot;
    504                 PassVisitor<FixUniqueIds_old> fui;
     472                PassVisitor<ForallPointerDecay_old> fpd;
    505473                type->accept( epc );
    506474                type->accept( lrt );
    507                 type->accept( te );
    508                 type->accept( af );
    509                 type->accept( cot );
    510                 type->accept( fui );
     475                type->accept( fpd );
    511476        }
    512477
     
    1019984        }
    1020985
    1021         /// Replace all traits in assertion lists with their assertions.
    1022         void expandTraits( std::list< TypeDecl * > & forall ) {
    1023                 for ( TypeDecl * type : forall ) {
    1024                         std::list< DeclarationWithType * > asserts;
    1025                         asserts.splice( asserts.end(), type->assertions );
    1026                         // expand trait instances into their members
    1027                         for ( DeclarationWithType * assertion : asserts ) {
    1028                                 if ( TraitInstType * traitInst = dynamic_cast< TraitInstType * >( assertion->get_type() ) ) {
    1029                                         // expand trait instance into all of its members
    1030                                         expandAssertions( traitInst, back_inserter( type->assertions ) );
    1031                                         delete traitInst;
    1032                                 } else {
    1033                                         // pass other assertions through
    1034                                         type->assertions.push_back( assertion );
    1035                                 } // if
    1036                         } // for
    1037                 }
    1038         }
    1039 
    1040         /// Fix each function in the assertion list and check for invalid void type.
    1041         void fixAssertions(
    1042                         std::list< TypeDecl * > & forall, BaseSyntaxNode * node ) {
    1043                 for ( TypeDecl * type : forall ) {
    1044                         for ( DeclarationWithType *& assertion : type->assertions ) {
    1045                                 bool isVoid = fixFunction( assertion );
    1046                                 if ( isVoid ) {
    1047                                         SemanticError( node, "invalid type void in assertion of function " );
    1048                                 } // if
    1049                         } // for
    1050                 }
    1051         }
    1052 
    1053986        void ForallPointerDecay_old::previsit( ObjectDecl * object ) {
    1054987                // ensure that operator names only apply to functions or function pointers
     
    10731006        void ForallPointerDecay_old::previsit( UnionDecl * aggrDecl ) {
    10741007                forallFixer( aggrDecl->parameters, aggrDecl );
    1075         }
    1076 
    1077         void TraitExpander_old::previsit( FunctionType * ftype ) {
    1078                 expandTraits( ftype->forall );
    1079         }
    1080 
    1081         void TraitExpander_old::previsit( StructDecl * aggrDecl ) {
    1082                 expandTraits( aggrDecl->parameters );
    1083         }
    1084 
    1085         void TraitExpander_old::previsit( UnionDecl * aggrDecl ) {
    1086                 expandTraits( aggrDecl->parameters );
    1087         }
    1088 
    1089         void AssertionFixer_old::previsit( FunctionType * ftype ) {
    1090                 fixAssertions( ftype->forall, ftype );
    1091         }
    1092 
    1093         void AssertionFixer_old::previsit( StructDecl * aggrDecl ) {
    1094                 fixAssertions( aggrDecl->parameters, aggrDecl );
    1095         }
    1096 
    1097         void AssertionFixer_old::previsit( UnionDecl * aggrDecl ) {
    1098                 fixAssertions( aggrDecl->parameters, aggrDecl );
    1099         }
    1100 
    1101         void CheckOperatorTypes_old::previsit( ObjectDecl * object ) {
    1102                 // ensure that operator names only apply to functions or function pointers
    1103                 if ( CodeGen::isOperator( object->name ) && ! dynamic_cast< FunctionType * >( object->type->stripDeclarator() ) ) {
    1104                         SemanticError( object->location, toCString( "operator ", object->name.c_str(), " is not a function or function pointer." )  );
    1105                 }
    1106         }
    1107 
    1108         void FixUniqueIds_old::previsit( DeclarationWithType * decl ) {
    1109                 decl->fixUniqueId();
    11101008        }
    11111009
  • src/SymTab/Validate.h

    r92538ab r4559b34  
    4242        void validate_E( std::list< Declaration * > &translationUnit );
    4343        void validate_F( std::list< Declaration * > &translationUnit );
     44        void decayForallPointers( std::list< Declaration * > & translationUnit );
    4445
    4546        const ast::Type * validateType(
  • src/Tuples/TupleAssignment.cc

    r92538ab r4559b34  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar 16 14:06:00 2022
    13 // Update Count     : 10
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Fri Dec 13 23:45:33 2019
     13// Update Count     : 9
    1414//
    1515
     
    465465                                        // resolve ctor/dtor for the new object
    466466                                        ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
    467                                                         InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context );
     467                                                        InitTweak::genCtorInit( location, ret ), spotter.crntFinder.localSyms );
    468468                                        // remove environments from subexpressions of stmtExpr
    469469                                        ast::Pass< EnvRemover > rm{ env };
     
    560560                                        // resolve the cast expression so that rhsCand return type is bound by the cast
    561561                                        // type as needed, and transfer the resulting environment
    562                                         ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env );
     562                                        ResolvExpr::CandidateFinder finder{ spotter.crntFinder.localSyms, env };
    563563                                        finder.find( rhsCand->expr, ResolvExpr::ResolvMode::withAdjustment() );
    564564                                        assert( finder.candidates.size() == 1 );
     
    609609                                        // explode the LHS so that each field of a tuple-valued expr is assigned
    610610                                        ResolvExpr::CandidateList lhs;
    611                                         explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true );
     611                                        explode( *lhsCand, crntFinder.localSyms, back_inserter(lhs), true );
    612612                                        for ( ResolvExpr::CandidateRef & cand : lhs ) {
    613613                                                // each LHS value must be a reference - some come in with a cast, if not
     
    629629                                                        if ( isTuple( rhsCand->expr ) ) {
    630630                                                                // multiple assignment
    631                                                                 explode( *rhsCand, crntFinder.context.symtab, back_inserter(rhs), true );
     631                                                                explode( *rhsCand, crntFinder.localSyms, back_inserter(rhs), true );
    632632                                                                matcher.reset(
    633633                                                                        new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } );
     
    648648                                                        // multiple assignment
    649649                                                        ResolvExpr::CandidateList rhs;
    650                                                         explode( rhsCand, crntFinder.context.symtab, back_inserter(rhs), true );
     650                                                        explode( rhsCand, crntFinder.localSyms, back_inserter(rhs), true );
    651651                                                        matcher.reset(
    652652                                                                new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } );
     
    678678                                )
    679679
    680                                 ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env );
     680                                ResolvExpr::CandidateFinder finder{ crntFinder.localSyms, matcher->env };
    681681
    682682                                try {
  • src/Validate/Autogen.cpp

    r92538ab r4559b34  
    248248                structInst.params.push_back( new ast::TypeExpr(
    249249                        typeDecl->location,
    250                         new ast::TypeInstType( typeDecl )
     250                        new ast::TypeInstType( typeDecl->name, typeDecl )
    251251                ) );
    252252        }
     
    264264                unionInst.params.push_back( new ast::TypeExpr(
    265265                        unionDecl->location,
    266                         new ast::TypeInstType( typeDecl )
     266                        new ast::TypeInstType( typeDecl->name, typeDecl )
    267267                ) );
    268268        }
  • src/Validate/FindSpecialDeclsNew.cpp

    r92538ab r4559b34  
    3030
    3131struct FindDeclsCore : public ast::WithShortCircuiting {
    32         ast::TranslationGlobal & global;
    33         FindDeclsCore( ast::TranslationGlobal & g ) : global( g ) {}
     32        ast::TranslationUnit::Global & global;
     33        FindDeclsCore( ast::TranslationUnit::Global & g ) : global( g ) {}
    3434
    3535        void previsit( const ast::Decl * decl );
     
    7474        ast::Pass<FindDeclsCore>::run( translationUnit, translationUnit.global );
    7575
     76        // TODO: When everything gets the globals from the translation unit,
     77        // remove these.
     78        ast::dereferenceOperator = translationUnit.global.dereference;
     79        ast::dtorStruct = translationUnit.global.dtorStruct;
     80        ast::dtorStructDestroy = translationUnit.global.dtorDestroy;
     81
    7682        // TODO: conditionally generate 'fake' declarations for missing features,
    7783        // so that translation can proceed in the event that builtins, prelude,
  • src/Validate/module.mk

    r92538ab r4559b34  
    2020        Validate/CompoundLiteral.cpp \
    2121        Validate/CompoundLiteral.hpp \
    22         Validate/ForallPointerDecay.cpp \
    23         Validate/ForallPointerDecay.hpp \
    2422        Validate/HandleAttributes.cc \
    2523        Validate/HandleAttributes.h \
  • src/Virtual/Tables.cc

    r92538ab r4559b34  
    1010// Created On       : Mon Aug 31 11:11:00 2020
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Mar 11 10:40:00 2022
    13 // Update Count     : 3
     12// Last Modified On : Wed Apr 21 15:36:00 2021
     13// Update Count     : 2
    1414//
    1515
    16 #include "AST/Attribute.hpp"
    17 #include "AST/Copy.hpp"
    18 #include "AST/Decl.hpp"
    19 #include "AST/Expr.hpp"
    20 #include "AST/Init.hpp"
    21 #include "AST/Stmt.hpp"
    22 #include "AST/Type.hpp"
    2316#include <SynTree/Attribute.h>
    2417#include <SynTree/Declaration.h>
     
    8477}
    8578
    86 static ast::ObjectDecl * makeVtableDeclaration(
    87                 CodeLocation const & location, std::string const & name,
    88                 ast::StructInstType const * type, ast::Init const * init ) {
    89         ast::Storage::Classes storage;
    90         if ( nullptr == init ) {
    91                 storage.is_extern = true;
    92         }
    93         return new ast::ObjectDecl(
    94                 location,
    95                 name,
    96                 type,
    97                 init,
    98                 storage,
    99                 ast::Linkage::Cforall
    100         );
    101 }
    102 
    10379ObjectDecl * makeVtableForward( std::string const & name, StructInstType * type ) {
    10480        assert( type );
    10581        return makeVtableDeclaration( name, type, nullptr );
    106 }
    107 
    108 ast::ObjectDecl * makeVtableForward(
    109                 CodeLocation const & location, std::string const & name,
    110                 ast::StructInstType const * vtableType ) {
    111         assert( vtableType );
    112         return makeVtableDeclaration( location, name, vtableType, nullptr );
    11382}
    11483
     
    154123}
    155124
    156 static std::vector<ast::ptr<ast::Init>> buildInits(
    157                 CodeLocation const & location,
    158                 //std::string const & name,
    159                 ast::StructInstType const * vtableType,
    160                 ast::Type const * objectType ) {
    161         ast::StructDecl const * vtableStruct = vtableType->base;
    162 
    163         std::vector<ast::ptr<ast::Init>> inits;
    164         inits.reserve( vtableStruct->members.size() );
    165 
    166         // This is designed to run before the resolver.
    167         for ( auto field : vtableStruct->members ) {
    168                 if ( std::string( "parent" ) == field->name ) {
    169                         // This will not work with polymorphic state.
    170                         auto oField = field.strict_as<ast::ObjectDecl>();
    171                         auto fieldType = oField->type.strict_as<ast::PointerType>();
    172                         auto parentType = fieldType->base.strict_as<ast::StructInstType>();
    173                         std::string const & parentInstance = instanceName( parentType->name );
    174                         inits.push_back(
    175                                         new ast::SingleInit( location, new ast::AddressExpr( new ast::NameExpr( location, parentInstance ) ) ) );
    176                 } else if ( std::string( "__cfavir_typeid" ) == field->name ) {
    177                         std::string const & baseType = baseTypeName( vtableType->name );
    178                         std::string const & typeId = typeIdName( baseType );
    179                         inits.push_back( new ast::SingleInit( location, new ast::AddressExpr( new ast::NameExpr( location, typeId ) ) ) );
    180                 } else if ( std::string( "size" ) == field->name ) {
    181                         inits.push_back( new ast::SingleInit( location, new ast::SizeofExpr( location, objectType )
    182                         ) );
    183                 } else if ( std::string( "align" ) == field->name ) {
    184                         inits.push_back( new ast::SingleInit( location,
    185                                 new ast::AlignofExpr( location, objectType )
    186                         ) );
    187                 } else {
    188                         inits.push_back( new ast::SingleInit( location,
    189                                 new ast::NameExpr( location, field->name )
    190                         ) );
    191                 }
    192                 //ast::Expr * expr = buildInitExpr(...);
    193                 //inits.push_back( new ast::SingleInit( location, expr ) )
    194         }
    195 
    196         return inits;
    197 }
    198 
    199 ast::ObjectDecl * makeVtableInstance(
    200                 CodeLocation const & location,
    201                 std::string const & name,
    202                 ast::StructInstType const * vtableType,
    203                 ast::Type const * objectType,
    204                 ast::Init const * init ) {
    205         assert( vtableType );
    206         assert( objectType );
    207 
    208         // Build the initialization.
    209         if ( nullptr == init ) {
    210                 init = new ast::ListInit( location,
    211                         buildInits( location, vtableType, objectType ) );
    212 
    213         // The provided init should initialize everything except the parent
    214         // pointer, the size-of and align-of fields. These should be inserted.
    215         } else {
    216                 // Except this is not yet supported.
    217                 assert(false);
    218         }
    219         return makeVtableDeclaration( location, name, vtableType, init );
    220 }
    221 
    222125namespace {
    223126        std::string const functionName = "get_exception_vtable";
     
    237140                new ReferenceType( noQualifiers, vtableType ),
    238141                nullptr,
    239                 { new Attribute("unused") }
     142        { new Attribute("unused") }
    240143        ) );
    241144        type->parameters.push_back( new ObjectDecl(
     
    257160}
    258161
    259 ast::FunctionDecl * makeGetExceptionForward(
    260                 CodeLocation const & location,
    261                 ast::Type const * vtableType,
    262                 ast::Type const * exceptType ) {
    263         assert( vtableType );
    264         assert( exceptType );
    265         return new ast::FunctionDecl(
    266                 location,
    267                 functionName,
    268                 { /* forall */ },
    269                 { new ast::ObjectDecl(
    270                         location,
    271                         "__unused",
    272                         new ast::PointerType( exceptType )
    273                 ) },
    274                 { new ast::ObjectDecl(
    275                         location,
    276                         "_retvalue",
    277                         new ast::ReferenceType( vtableType )
    278                 ) },
    279                 nullptr,
    280                 ast::Storage::Classes(),
    281                 ast::Linkage::Cforall,
    282                 { new ast::Attribute( "unused" ) }
    283         );
    284 }
    285 
    286162FunctionDecl * makeGetExceptionFunction(
    287163                ObjectDecl * vtableInstance, Type * exceptType ) {
     
    292168        func->statements = new CompoundStmt( {
    293169                new ReturnStmt( new VariableExpr( vtableInstance ) ),
    294         } );
    295         return func;
    296 }
    297 
    298 ast::FunctionDecl * makeGetExceptionFunction(
    299                 CodeLocation const & location,
    300                 ast::ObjectDecl const * vtableInstance, ast::Type const * exceptType ) {
    301         assert( vtableInstance );
    302         assert( exceptType );
    303         ast::FunctionDecl * func = makeGetExceptionForward(
    304                         location, ast::deepCopy( vtableInstance->type ), exceptType );
    305         func->stmts = new ast::CompoundStmt( location, {
    306                 new ast::ReturnStmt( location, new ast::VariableExpr( location, vtableInstance ) )
    307170        } );
    308171        return func;
     
    328191}
    329192
    330 ast::ObjectDecl * makeTypeIdInstance(
    331                 CodeLocation const & location,
    332                 ast::StructInstType const * typeIdType ) {
    333         assert( typeIdType );
    334         ast::StructInstType * type = ast::mutate( typeIdType );
    335         type->set_const( true );
    336         std::string const & typeid_name = typeIdTypeToInstance( typeIdType->name );
    337         return new ast::ObjectDecl(
    338                 location,
    339                 typeid_name,
    340                 type,
    341                 new ast::ListInit( location, {
    342                         new ast::SingleInit( location,
    343                                 new ast::AddressExpr( location,
    344                                         new ast::NameExpr( location, "__cfatid_exception_t" ) ) )
    345                 } ),
    346                 ast::Storage::Classes(),
    347                 ast::Linkage::Cforall,
    348                 nullptr,
    349                 { new ast::Attribute( "cfa_linkonce" ) }
    350         );
    351193}
    352 
    353 }
  • src/Virtual/Tables.h

    r92538ab r4559b34  
    1010// Created On       : Mon Aug 31 11:07:00 2020
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Wec Dec  8 16:58:00 2021
    13 // Update Count     : 3
     12// Last Modified On : Wed Apr 21 10:30:00 2021
     13// Update Count     : 2
    1414//
    1515
    1616#include <list>  // for list
    1717
    18 #include <string>
    19 #include "AST/Fwd.hpp"
    2018class Declaration;
    2119class StructDecl;
     
    3735 * vtableType node is consumed.
    3836 */
    39 ast::ObjectDecl * makeVtableForward(
    40         CodeLocation const & location, std::string const & name,
    41         ast::StructInstType const * vtableType );
    4237
    4338ObjectDecl * makeVtableInstance(
     
    4843 * vtableType and init (if provided) nodes are consumed.
    4944 */
    50 ast::ObjectDecl * makeVtableInstance(
    51         CodeLocation const & location,
    52         std::string const & name,
    53         ast::StructInstType const * vtableType,
    54         ast::Type const * objectType,
    55         ast::Init const * init = nullptr );
    5645
    5746// Some special code for how exceptions interact with virtual tables.
     
    6049 * linking the vtableType to the exceptType. Both nodes are consumed.
    6150 */
    62 ast::FunctionDecl * makeGetExceptionForward(
    63         CodeLocation const & location,
    64         ast::Type const * vtableType,
    65         ast::Type const * exceptType );
    6651
    6752FunctionDecl * makeGetExceptionFunction(
     
    7055 * exceptType node is consumed.
    7156 */
    72 ast::FunctionDecl * makeGetExceptionFunction(
    73         CodeLocation const & location,
    74         ast::ObjectDecl const * vtableInstance, ast::Type const * exceptType );
    7557
    7658ObjectDecl * makeTypeIdInstance( StructInstType const * typeIdType );
     
    7860 * TODO: Should take the parent type. Currently locked to the exception_t.
    7961 */
    80 ast::ObjectDecl * makeTypeIdInstance(
    81         const CodeLocation & location, ast::StructInstType const * typeIdType );
    8262
    8363}
  • src/main.cc

    r92538ab r4559b34  
    1010// Created On       : Fri May 15 23:12:02 2015
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Mar 11 10:39:00 2022
    13 // Update Count     : 671
     12// Last Modified On : Wed Jan 26 14:09:00 2022
     13// Update Count     : 670
    1414//
    1515
     
    7676#include "Validate/Autogen.hpp"             // for autogenerateRoutines
    7777#include "Validate/FindSpecialDecls.h"      // for findGlobalDecls
    78 #include "Validate/ForallPointerDecay.hpp"  // for decayForallPointers
    7978#include "Validate/CompoundLiteral.hpp"     // for handleCompoundLiterals
    8079#include "Validate/InitializerLength.hpp"   // for setLengthFromInitializer
     
    332331
    333332                if( useNewAST ) {
     333                        PASS( "Apply Concurrent Keywords", Concurrency::applyKeywords( translationUnit ) );
     334                        PASS( "Forall Pointer Decay", SymTab::decayForallPointers( translationUnit ) );
    334335                        CodeTools::fillLocations( translationUnit );
    335336
     
    341342
    342343                        forceFillCodeLocations( transUnit );
    343 
    344                         PASS( "Implement Concurrent Keywords", Concurrency::implementKeywords( transUnit ) );
    345 
    346                         // Must be after implement concurrent keywords; because uniqueIds
    347                         //   must be set on declaration before resolution.
    348                         // Must happen before autogen routines are added.
    349                         PASS( "Forall Pointer Decay", Validate::decayForallPointers( transUnit ) );
    350344
    351345                        // Must happen before autogen routines are added.
     
    493487                        PASS( "Translate Tries" , ControlStruct::translateTries( translationUnit ) );
    494488                }
     489
     490               
    495491
    496492                PASS( "Gen Waitfor" , Concurrency::generateWaitFor( translationUnit ) );
  • tests/.expect/declarationSpecifier.arm64.txt

    r92538ab r4559b34  
    11321132char **_X13cfa_args_argvPPc_1;
    11331133char **_X13cfa_args_envpPPc_1;
    1134 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
     1134signed int _X17cfa_main_returnedi_1 = ((signed int )0);
    11351135signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    11361136    __attribute__ ((unused)) signed int _X12_retval_maini_1;
     
    11491149    signed int _tmp_cp_ret6;
    11501150    signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6);
    1151     if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
    1152         {
    1153             ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    1154         }
    1155 
     1151    {
     1152        ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    11561153    }
    11571154
  • tests/.expect/gccExtensions.arm64.txt

    r92538ab r4559b34  
    324324char **_X13cfa_args_argvPPc_1;
    325325char **_X13cfa_args_envpPPc_1;
    326 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
     326signed int _X17cfa_main_returnedi_1 = ((signed int )0);
    327327signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    328328    __attribute__ ((unused)) signed int _X12_retval_maini_1;
     
    341341    signed int _tmp_cp_ret6;
    342342    signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6);
    343     if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
    344         {
    345             ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    346         }
    347 
     343    {
     344        ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    348345    }
    349346
  • tests/.expect/random.arm64.txt

    r92538ab r4559b34  
    11õ
    22=
    3 K
     3V
    44-911259971
    556
    6 11
     6-4
    771232105397
    880
    9 11
     918
    1010-914096085
    11111
    12 20
     1215
    13132077092859
    14141
    15 12
     1511
    16160.677254
    17170.678106775246139
  • tests/Makefile.am

    r92538ab r4559b34  
    2828DEBUG_FLAGS=-debug -g -O0
    2929
    30 quick_test=avl_test operators numericConstants expression enum array typeof cast raii/dtor-early-exit raii/init_once attributes meta/dumpable
     30quick_test=avl_test operators numericConstants expression enum array typeof cast raii/dtor-early-exit raii/init_once attributes
    3131
    3232concurrent=
     
    6666PRETTY_PATH=mkdir -p $(dir $(abspath ${@})) && cd ${srcdir} &&
    6767
    68 .PHONY: list .validate .test_makeflags
    69 .INTERMEDIATE: .validate .validate.cfa .test_makeflags
     68.PHONY: list .validate
     69.INTERMEDIATE: .validate .validate.cfa
    7070EXTRA_PROGRAMS = avl_test linkonce .dummy_hack # build but do not install
    7171EXTRA_DIST = test.py \
     
    123123        @+${TEST_PY} --list ${concurrent}
    124124
    125 .test_makeflags:
    126         @echo "${MAKEFLAGS}"
    127 
    128125.validate: .validate.cfa
    129126        $(CFACOMPILE) .validate.cfa -fsyntax-only -Wall -Wextra -Werror
  • tests/PRNG.cfa

    r92538ab r4559b34  
    88// Created On       : Wed Dec 29 09:38:12 2021
    99// Last Modified By : Peter A. Buhr
    10 // Last Modified On : Sat Apr  9 15:21:14 2022
    11 // Update Count     : 344
     10// Last Modified On : Fri Feb 11 08:16:43 2022
     11// Update Count     : 328
    1212//
    1313
     
    2020#include <malloc.h>                                                                             // malloc_stats
    2121#include <locale.h>                                                                             // setlocale
    22 #include <mutex_stmt.hfa>
     22
     23// FIX ME: spurious characters appear in output
     24Duration default_preemption() { return 0; }
    2325
    2426#ifdef TIME                                                                                             // use -O2 -nodebug
     
    4850        } // for
    4951        double std = sqrt( sum / BUCKETS );
    50         mutex( sout ) sout | "trials"  | TRIALS | "buckets" | BUCKETS
    51                 | "min" | min | "max" | max
    52                 | "avg" | wd(0,1, avg) | "std" | wd(0,1, std) | "rstd" | wd(0,1, (avg == 0 ? 0.0 : std / avg * 100)) | "%";
     52        sout | "trials"  | TRIALS | "buckets" | BUCKETS
     53                 | "min" | min | "max" | max
     54                 | "avg" | wd(0,1, avg) | "std" | wd(0,1, std) | "rstd" | wd(0,1, (avg == 0 ? 0.0 : std / avg * 100)) | "%";
    5355} // avgstd
    5456
    55 
    5657uint32_t seed = 1009;
     58
    5759
    5860thread T1 {};
     
    9294        unsigned int * buckets = calloc( BUCKETS );                     // too big for task stack
    9395        for ( TRIALS ) {
    94                 buckets[prng( th ) % BUCKETS] += 1;     // concurrent
     96                buckets[prng( (thread$ &)th ) % BUCKETS] += 1;  // concurrent
    9597        } // for
    9698        avgstd( buckets );
  • tests/collections/.expect/string-api-coverage.txt

    r92538ab r4559b34  
    11hello hello hello
    2 
    3 hello
    42true false
    53true false
  • tests/collections/.expect/string-gc.txt

    r92538ab r4559b34  
    3838x from 5 to 15
    3939y from 5 to 15
    40 ======================== fillNoCompact
    41 about to expand, a = aaa
    42 expanded, a = aaa
    43 about to expand, a = aaa
    44 expanded, a = aaa
    45 about to expand, a = aaa
    46 expanded, a = aaa
    47 about to expand, a = aaa
    48 expanded, a = aaa
    49 about to expand, a = aaa
    50 expanded, a = aaa
  • tests/collections/.expect/vector-err-pass-perm-it-byval.txt

    r92538ab r4559b34  
    1 collections/vector-demo.cfa:95:1 error: Unique best alternative includes deleted identifier in Generated Cast of:
     1error: Unique best alternative includes deleted identifier in Generated Cast of:
    22  Application of
    33    Deleted Expression
  • tests/collections/string-api-coverage.cfa

    r92538ab r4559b34  
    11#include <containers/string.hfa>
    2 #include <string_sharectx.hfa>
    32
    43void assertWellFormedHandleList( int maxLen ) { // with(HeapArea)
     
    2625
    2726int main () {
    28 
    29     #ifdef STRING_SHARING_OFF
    30     string_sharectx c = { NO_SHARING };
    31     #endif
    32 
    3327    string s = "hello";
    3428    string s2 = "hello";
     
    3731
    3832    // IO operator, x2
    39     sout | s | s | s;  // hello hello hello
    40 
    41     // empty ctor then assign
    42     string sxx;
    43     sout | sxx;  // (blank line)
    44     sxx = s;
    45     sout | sxx;  // hello
     33    sout | s | s | s;
    4634
    4735    // Comparisons
  • tests/collections/string-gc.cfa

    r92538ab r4559b34  
    22
    33size_t bytesRemaining() {
    4     return DEBUG_string_bytes_avail_until_gc( DEBUG_string_heap() );
     4    return DEBUG_string_bytes_avail_until_gc( DEBUG_string_heap );
    55}
    66
    77size_t heapOffsetStart( string_res & s ) {
    8     const char * startByte = DEBUG_string_heap_start( DEBUG_string_heap() );
     8    const char * startByte = DEBUG_string_heap_start( DEBUG_string_heap );
    99    assert( s.Handle.s >= startByte );
    1010    return s.Handle.s - startByte;
     
    120120}
    121121
    122 void fillNoCompact() {
    123     // show that allocating in a heap filled with mostly live strings (no collectable garbage) causes heap growth
    124 
    125     sout | "======================== fillNoCompact";
    126 
    127     size_t lastTimeBytesAvail = bytesRemaining();
    128     assert( lastTimeBytesAvail >= 200 ); // starting this test with nontrivial room
    129 
    130     // mostly fill the pad
    131     string_res a = "aaa";  // will have to be moved
    132     string_res z = "zzz";
    133     for (i; 5) {
    134         while ( bytesRemaining() > 10 ) {
    135             z += ".";
    136         }
    137         sout | "about to expand, a = " | a;
    138         while ( bytesRemaining() <= 10 ) {
    139             z += ".";
    140         }
    141         sout | "expanded, a = " | a;
    142 
    143         // each growth gives more usable space than the last
    144         assert( bytesRemaining() > lastTimeBytesAvail );
    145         lastTimeBytesAvail = bytesRemaining();
    146     }
    147 }
    148 
    149122int main() {
    150123    basicFillCompact();
    151124    fillCompact_withSharedEdits();
    152     fillNoCompact();
    153125}
  • tests/collections/string-overwrite.cfa

    r92538ab r4559b34  
    11#include <containers/string.hfa>
    2 #include <string_sharectx.hfa>
    32
    43/*
     
    1211WE = witness end
    1312
    14 The test does:
     13The dest does:
    1514  starts with the entire string being, initially, the alphabet; prints this entire alphabet
    1615  sets up modifier and witness as ranges within it, and prints a visualization of those ranges
     
    2524This API's convention has Start positions being inclusive and end positions being exclusive.
    2625
    27                                 v Case number in output
    2826With 1 equivalence class:
    2927MS = ME = WS = WE               1
     
    120118    struct { int ms; int me; int ws; int we; char *replaceWith; char *label; } cases[] = {
    121119        { 12, 14, 10, 20, "xxxxx", "warmup" },
     120//        { 12, 14, 12, 14, "xxxxx", ""       },  // the bug that got me into this test (should be a dup with case 6)
    122121        { 10, 10, 10, 10, "=====", "1"      },
    123122        { 10, 10, 10, 10, "=="   , ""       },
     
    224223        { 12, 14, 10, 16, "="    , ""       },
    225224        { 12, 14, 10, 16, ""     , ""       },
     225/*
     226        { , , , , "=====", "NN"     },
     227        {  "=="   , ""       },
     228        {  "="    , ""       },
     229        {  ""     , ""       },
     230*/
    226231    };
    227232    for ( i; sizeof(cases)/sizeof(cases[0]) ) {
     
    233238
    234239
     240// void f( string & s, string & toEdit ) {
     241
     242//     sout | s | "|" | toEdit | "|";
     243
     244//     s(14, 16) = "-";
     245//     sout | s | "|" | toEdit | "|";
     246// }
     247
    235248int main() {
    236 
    237     #ifdef STRING_SHARING_OFF
    238     string_sharectx c = { NO_SHARING };
    239     #endif
    240 
    241 
    242249    //          0         1         2
    243250    //          01234567890123456789012345
  • tests/concurrent/mutexstmt/.expect/locks.txt

    r92538ab r4559b34  
    33Start Test: multi lock deadlock/mutual exclusion
    44End Test: multi lock deadlock/mutual exclusion
    5 Start Test: multi polymorphic lock deadlock/mutual exclusion
    6 End Test: multi polymorphic lock deadlock/mutual exclusion
     5Start Test: single scoped lock mutual exclusion
     6End Test: single scoped lock mutual exclusion
     7Start Test: multi scoped lock deadlock/mutual exclusion
     8End Test: multi scoped lock deadlock/mutual exclusion
  • tests/concurrent/mutexstmt/locks.cfa

    r92538ab r4559b34  
    33
    44const unsigned int num_times = 10000;
    5 
    6 Duration default_preemption() { return 0; }
    75
    86single_acquisition_lock m1, m2, m3, m4, m5;
     
    2422}
    2523
    26 void refTest( single_acquisition_lock & m ) {
    27         mutex ( m ) {
    28                 assert(!insideFlag);
    29                 insideFlag = true;
    30                 assert(insideFlag);
    31                 insideFlag = false;
    32         }
    33 }
    34 
    3524thread T_Multi {};
    3625
    3726void main( T_Multi & this ) {
    3827        for (unsigned int i = 0; i < num_times; i++) {
    39                 refTest( m1 );
    4028                mutex ( m1 ) {
    4129                        assert(!insideFlag);
     
    7159}
    7260
    73 single_acquisition_lock l1;
    74 linear_backoff_then_block_lock l2;
    75 owner_lock l3;
     61thread T_Mutex_Scoped {};
    7662
    77 monitor monitor_t {};
    78 
    79 monitor_t l4;
    80 
    81 thread T_Multi_Poly {};
    82 
    83 void main( T_Multi_Poly & this ) {
     63void main( T_Mutex_Scoped & this ) {
    8464        for (unsigned int i = 0; i < num_times; i++) {
    85                 refTest( l1 );
    86                 mutex ( l1, l4 ) {
     65                {
     66                        scoped_lock(single_acquisition_lock) s{m1};
     67                        count++;
     68                }
     69                {
     70                        scoped_lock(single_acquisition_lock) s{m1};
    8771                        assert(!insideFlag);
    8872                        insideFlag = true;
     
    9074                        insideFlag = false;
    9175                }
    92                 mutex ( l1, l2, l3 ) {
     76        }
     77}
     78
     79thread T_Multi_Scoped {};
     80
     81void main( T_Multi_Scoped & this ) {
     82        for (unsigned int i = 0; i < num_times; i++) {
     83                {
     84                        scoped_lock(single_acquisition_lock) s{m1};
    9385                        assert(!insideFlag);
    9486                        insideFlag = true;
     
    9688                        insideFlag = false;
    9789                }
    98                 mutex ( l3, l1, l4 ) {
     90                {
     91                        scoped_lock(single_acquisition_lock) s1{m1};
     92                        scoped_lock(single_acquisition_lock) s2{m2};
     93                        scoped_lock(single_acquisition_lock) s3{m3};
     94                        scoped_lock(single_acquisition_lock) s4{m4};
     95                        scoped_lock(single_acquisition_lock) s5{m5};
    9996                        assert(!insideFlag);
    10097                        insideFlag = true;
     
    10299                        insideFlag = false;
    103100                }
    104                 mutex ( l1, l2, l4 ) {
     101                {
     102                        scoped_lock(single_acquisition_lock) s1{m1};
     103                        scoped_lock(single_acquisition_lock) s3{m3};
     104                        assert(!insideFlag);
     105                        insideFlag = true;
     106                        assert(insideFlag);
     107                        insideFlag = false;
     108                }
     109                {
     110                        scoped_lock(single_acquisition_lock) s1{m1};
     111                        scoped_lock(single_acquisition_lock) s2{m2};
     112                        scoped_lock(single_acquisition_lock) s4{m4};
     113                        assert(!insideFlag);
     114                        insideFlag = true;
     115                        assert(insideFlag);
     116                        insideFlag = false;
     117                }
     118                {
     119                        scoped_lock(single_acquisition_lock) s1{m1};
     120                        scoped_lock(single_acquisition_lock) s3{m3};
     121                        scoped_lock(single_acquisition_lock) s4{m4};
     122                        scoped_lock(single_acquisition_lock) s5{m5};
    105123                        assert(!insideFlag);
    106124                        insideFlag = true;
     
    113131int num_tasks = 10;
    114132int main() {
    115         processor p[num_tasks - 1];
     133        processor p[10];
    116134
    117135        printf("Start Test: single lock mutual exclusion\n");
    118136        {
    119                 T_Mutex t[num_tasks];
     137                T_Mutex t[10];
    120138        }
    121139        assert(count == num_tasks * num_times);
     
    123141        printf("Start Test: multi lock deadlock/mutual exclusion\n");
    124142        {
    125                 T_Multi t[num_tasks];
     143                T_Multi t[10];
    126144        }
    127145        printf("End Test: multi lock deadlock/mutual exclusion\n");
    128         printf("Start Test: multi polymorphic lock deadlock/mutual exclusion\n");
     146       
     147        count = 0;
     148        printf("Start Test: single scoped lock mutual exclusion\n");
    129149        {
    130                 T_Multi_Poly t[num_tasks];
     150                T_Mutex_Scoped t[10];
    131151        }
    132         printf("End Test: multi polymorphic lock deadlock/mutual exclusion\n");
     152        assert(count == num_tasks * num_times);
     153        printf("End Test: single scoped lock mutual exclusion\n");
     154        printf("Start Test: multi scoped lock deadlock/mutual exclusion\n");
     155        {
     156                T_Multi_Scoped t[10];
     157        }
     158        printf("End Test: multi scoped lock deadlock/mutual exclusion\n");     
    133159}
  • tests/designations.cfa

    r92538ab r4559b34  
    1010// Created On       : Thu Jun 29 15:26:36 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Mar 28 22:41:55 2022
    13 // Update Count     : 15
     12// Last Modified On : Thu Jul 27 11:46:35 2017
     13// Update Count     : 3
    1414//
    1515
     
    1818// is used for the designation syntax
    1919#ifdef __cforall
    20 #define _ :
    21 #define AT @
     20#define DES :
    2221#else
    23 int printf( const char *, ...);
    24 #define _ =
    25 #define AT
     22int printf(const char *, ...);
     23#define DES =
    2624#endif
    2725
    2826const int indentAmt = 2;
    29 void indent( int level ) {
    30         for ( int i = 0; i < level; ++i ) {
    31                 printf( " " );
     27void indent(int level) {
     28        for (int i = 0; i < level; ++i) {
     29                printf(" ");
    3230        }
    3331}
     
    3836        int * ptr;
    3937};
    40 void printA( struct A a, int level ) {
    41         indent( level );
    42         printf( "(A){ %d %d %p }\n", a.x, a.y, a.ptr );
     38void printA(struct A a, int level) {
     39        indent(level);
     40        printf("(A){ %d %d %p }\n", a.x, a.y, a.ptr);
    4341}
    4442
     
    4745        struct A a0, a1;
    4846};
    49 void printB( struct B b, int level ) {
    50         indent( level );
    51         printf( "(B){\n" );
    52         printA( b.a0, level+indentAmt );
    53         printA( b.a1, level+indentAmt );
    54         indent( level );
    55         printf( "}\n" );
     47void printB(struct B b, int level) {
     48        indent(level);
     49        printf("(B){\n");
     50        printA(b.a0, level+indentAmt);
     51        printA(b.a1, level+indentAmt);
     52        indent(level);
     53        printf("}\n");
    5654}
    5755
     
    6159        struct B b;
    6260};
    63 void printC( struct C c, int level ) {
    64         indent( level );
    65         printf( "(C){\n" );
    66         indent( level+indentAmt );
    67         printf( "(int[]{ %d %d %d }\n", c.arr[0], c.arr[1], c.arr[2]);
    68         printB( c.b, level+indentAmt );
    69         indent( level );
    70         printf( "}\n" );
     61void printC(struct C c, int level) {
     62        indent(level);
     63        printf("(C){\n");
     64        indent(level+indentAmt);
     65        printf("(int[]{ %d %d %d }\n", c.arr[0], c.arr[1], c.arr[2]);
     66        printB(c.b, level+indentAmt);
     67        indent(level);
     68        printf("}\n");
    7169}
    7270
     
    7775        };
    7876};
    79 void printD( struct D d, int level ) {
    80         indent( level);
    81         printf( "(D ){ %d }\n", d.x );
     77void printD(struct D d, int level) {
     78        indent(level);
     79        printf("(D){ %d }\n", d.x);
    8280}
    8381
     
    10199    } m;
    102100};
    103 struct Fred s1 AT= { .m.j _ 3 };
    104 struct Fred s2 AT= { .i _ { [2] _ 2 } };
     101struct Fred s1 @= { .m.j : 3 };
     102struct Fred s2 @= { .i : { [2] : 2 } };
    105103
    106104int main() {
    107105        // simple designation case - starting from beginning of structure, leaves ptr default-initialized (zero)
    108106        struct A y0 = {
    109                 .x _ 2,
    110                 .y _ 3
     107                .x DES 2,
     108                .y DES 3
    111109        };
    112110
     
    119117        // use designation to move to member y, leaving x default-initialized (zero)
    120118        struct A y2 = {
    121                 .y _ 3,
     119                .y DES 3,
    122120                0
    123121        };
     
    129127#endif
    130128
    131         printf( "=====A=====\n" );
    132         printA( y0, 0 );
    133         printA( y1, 0 );
    134         printA( y2, 0 );
    135         printf( "=====A=====\n\n" );
     129        printf("=====A=====\n");
     130        printA(y0, 0);
     131        printA(y1, 0);
     132        printA(y2, 0);
     133        printf("=====A=====\n\n");
    136134
    137135        // initialize only first element (z0.a.x), leaving everything else default-initialized (zero), no nested curly-braces
     
    142140                { 3 }, // z1.a0
    143141                { 4 }, // z1.a1
    144                 .a0 _ { 5 }, // z1.a0
     142                .a0 DES { 5 }, // z1.a0
    145143                { 6 }, // z1.a1
    146                 .a0.y _ 2, // z1.a0.y
     144                .a0.y DES 2, // z1.a0.y
    147145                0, // z1.a0.ptr
    148146        };
     
    172170        };
    173171
    174         printf( "=====B=====\n" );
    175         printB( z0, 0 );
    176         printB( z1, 0 );
    177         printB( z2, 0 );
    178         printB( z3, 0 );
    179         printB( z5, 0 );
    180         printB( z6, 0 );
    181         printf( "=====B=====\n\n" );
     172        printf("=====B=====\n");
     173        printB(z0, 0);
     174        printB(z1, 0);
     175        printB(z2, 0);
     176        printB(z3, 0);
     177        printB(z5, 0);
     178        printB(z6, 0);
     179        printf("=====B=====\n\n");
    182180
    183181        // TODO: what about extra things in a nested init? are empty structs skipped??
     
    190188        };
    191189
    192         printf( "=====C=====\n" );
    193         printC( c1, 0 );
    194         printf( "=====C=====\n\n" );
     190        printf("=====C=====\n");
     191        printC(c1, 0);
     192        printf("=====C=====\n\n");
    195193
    196194#if ERROR
     
    215213#endif
    216214        // array designation
    217         int i[2] = { [1] _ 3 };
     215        int i[2] = { [1] : 3 };
    218216        // allowed to have 'too many' initialized lists - essentially they are ignored.
    219217        int i1 = { 3 };
     
    221219        // doesn't work yet.
    222220        // designate unnamed object's members
    223         // struct D d = { .x _ 3 };
     221        // struct D d = { .x DES 3 };
    224222#if ERROR
    225         struct D d1 = { .y _ 3 };
     223        struct D d1 = { .y DES 3 };
    226224#endif
    227225
     
    243241        // move cursor to e4.b.a0.x and initialize until e3.b.a1.ptr inclusive
    244242        union E e3 = {
    245                 .b.a0.x _ 2, 3, 0, 5, 6, 0
    246         };
    247 
    248         printf( "=====E=====\n" );
    249         printA( e0.a, 0 );
    250         printA( e1.a, 0 );
    251         printA( e2.a, 0 );
    252         printB( e3.b, 0 );
    253         printf( "=====E=====\n\n" );
     243                .b.a0.x DES 2, 3, 0, 5, 6, 0
     244        };
     245
     246        printf("=====E=====\n");
     247        printA(e0.a, 0);
     248        printA(e1.a, 0);
     249        printA(e2.a, 0);
     250        printB(e3.b, 0);
     251        printf("=====E=====\n\n");
    254252
    255253        // special case of initialization: char[] can be initialized with a string literal
    256254        const char * str0 = "hello";
    257255        char str1[] = "hello";
    258         const char c2[] = "abc";
    259         const char c3[] = { 'a', 'b', 'c' };
    260         const char c4[][2] = { { 'a', 'b' }, { 'c', 'd'}, { 'c', 'd'} };
    261 
    262         // more cases
    263 
    264 //      int widths[] = { [3 ... 9] _ 1, [10 ... 99] _ 2, [100] _ 3 };
    265 //      int widths[] = { [3 ~ 9] _ 1, [10 ~ 99] _ 2, [100] _ 3 };
    266         struct point { int x, y; };
    267         struct point p = { .y _ 5, .x _ 7 };
    268         union foo { int i; double d; };
    269         union foo f = { .d _ 4 };
    270         int v1, v2, v4;
    271         int w[6] = { [1] _ v1, v2, [4] _ v4 };
    272         int whitespace[256] = { [' '] _ 1, ['\t'] _ 1, ['\v'] _ 1, ['\f'] _ 1, ['\n'] _ 1, ['\r'] _ 1 };
    273         struct point ptarray[10] = { [2].y _ 34, [2].x _ 35, [0].x _ 36 };
     256        const char c1[] = "abc";
     257        const char c2[] = { 'a', 'b', 'c' };
     258        const char c3[][2] = { { 'a', 'b' }, { 'c', 'd'}, { 'c', 'd'} };
    274259}
    275260
  • tests/errors/.expect/scope.txt

    r92538ab r4559b34  
    44  double
    55... returning
    6   double
    7  with body
    8   Compound Statement:
     6  _retval_butThisIsAnError: double
     7  ... with attributes:
     8    Attribute with name: unused
    99
     10... with body
     11  CompoundStmt
     12
  • tests/io/io-acquire.cfa

    r92538ab r4559b34  
    1010// Created On       : Mon Mar  1 18:40:09 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Apr  9 15:22:03 2022
    13 // Update Count     : 76
     12// Last Modified On : Fri Jan 14 09:13:18 2022
     13// Update Count     : 74
    1414//
    1515
     
    1717#include <thread.hfa>
    1818#include <mutex_stmt.hfa>
     19
     20Duration default_preemption() { return 0; }
    1921
    2022thread T {};
  • tests/io/many_read.cfa

    r92538ab r4559b34  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // many_read.cfa -- Make sure that multiple concurrent reads don't mess up.
     7// many_read.cfa -- Make sure that multiple concurrent reads to mess up.
    88//
    99// Author           : Thierry Delisle
  • tests/meta/dumpable.cfa

    r92538ab r4559b34  
    1414//
    1515
     16#include <limits.h>
    1617#include <errno.h>
    17 #include <limits.h>
    18 #include <string.h>
    1918
    2019#include <fstream.hfa>
    2120
    2221extern "C" {
    23         #include <fcntl.h>
    24         #include <unistd.h>
    2522        #include <sys/prctl.h>
    2623        #include <sys/resource.h>
    2724        #include <sys/statvfs.h>
    28         #include <sys/stat.h>
    29         #include <sys/types.h>
     25        #include <unistd.h>
    3026}
    3127
     
    7672        }
    7773
    78         uint64_t avail = buf.f_bavail;
    79         avail *= buf.f_bsize;
    80         if(avail < 536870912_l64u) {
    81                 serr | "Available diskspace is less than ~500Mb: " | avail;
     74        if((buf.f_bsize * buf.f_bavail) < 536870912) {
     75                serr | "Available diskspace is less than ~500Mb: " | (buf.f_bsize * buf.f_bavail);
    8276        }
    8377
     
    106100}
    107101
    108 void check_core_pattern() {
    109         int ret;
    110         int cp = open("/proc/sys/kernel/core_pattern", 0, O_RDONLY);
    111         if(cp < 0) {
    112                 perror("open(/proc/sys/kernel/core_pattern, O_RDONLY) error");
    113                 return;
    114         }
    115 
    116         try {
    117                 const char * expected = "core\n";
    118                 const int sz = sizeof("core\n");
    119                 char buf[512];
    120                 ret = read(cp, buf, 512);
    121                 if(ret < 0) {
    122                         perror("first core pattern read error");
    123                         return;
    124                 }
    125                 ret = strncmp(expected, buf, sz - 1);
    126                 if(ret != 0) {
    127                         serr | "/proc/sys/kernel/core_pattern does not contain 'core', was:" | nl | nl | buf | nl
    128                              | "Test script expect cores files to be dumped with name 'core' in current working directory." | nl
    129                              | "Apport is not supported, it should be deactivated in /etc/default/apport for the test suite to work with core dumps.";
    130 
    131                         return;
    132                 }
    133         }
    134         finally {
    135                 ret = close(cp);
    136                 if(ret < 0) perror("close(/proc/sys/kernel/core_pattern) error");
    137         }
    138 
    139 }
    140 
    141102int main() {
    142103        check_ulimit();
     
    150111        check_dumpflag();
    151112
    152         check_core_pattern();
    153 
    154113        sout | "Done";
    155114}
  • tests/pybin/settings.py

    r92538ab r4559b34  
    155155        global generating
    156156        global make
    157         global make_jobfds
    158157        global output_width
    159158        global timeout
     
    169168        generating   = options.regenerate_expected
    170169        make         = ['make']
    171         make_jobfds  = []
    172170        output_width = 24
    173171        timeout      = Timeouts(options.timeout, options.global_timeout)
     
    179177                os.putenv('DISTCC_LOG', os.path.join(BUILDDIR, 'distcc_error.log'))
    180178
    181 def update_make_cmd(flags):
     179def update_make_cmd(force, jobs):
    182180        global make
    183         make = ['make', *flags]
    184 
    185 def update_make_fds(r, w):
    186         global make_jobfds
    187         make_jobfds = (r, w)
     181
     182        make = ['make'] if not force else ['make', "-j%i" % jobs]
    188183
    189184def validate():
     
    192187        global distcc
    193188        distcc       = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash()
    194         make_ret, out, err = tools.make( ".validate", output_file=subprocess.PIPE, error=subprocess.PIPE )
     189        errf = os.path.join(BUILDDIR, ".validate.err")
     190        make_ret, out = tools.make( ".validate", error_file = errf, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL )
    195191        if make_ret != 0:
     192                with open (errf, "r") as myfile:
     193                        error=myfile.read()
    196194                print("ERROR: Invalid configuration %s:%s" % (arch.string, debug.string), file=sys.stderr)
    197                 print("       verify returned : \n%s" % err, file=sys.stderr)
     195                print("       verify returned : \n%s" % error, file=sys.stderr)
     196                tools.rm(errf)
    198197                sys.exit(1)
     198
     199        tools.rm(errf)
    199200
    200201def prep_output(tests):
  • tests/pybin/tools.py

    r92538ab r4559b34  
    2323
    2424# helper functions to run terminal commands
    25 def sh(*cmd, timeout = False, output_file = None, input_file = None, input_text = None, error = subprocess.STDOUT, ignore_dry_run = False, pass_fds = []):
     25def sh(*cmd, timeout = False, output_file = None, input_file = None, input_text = None, error = subprocess.STDOUT, ignore_dry_run = False):
    2626        try:
    2727                cmd = list(cmd)
     
    6565                                **({'input' : bytes(input_text, encoding='utf-8')} if input_text else {'stdin' : input_file}),
    6666                                stdout  = output_file,
    67                                 stderr  = error,
    68                                 pass_fds = pass_fds
     67                                stderr  = error
    6968                        ) as proc:
    7069
    7170                                try:
    72                                         out, errout = proc.communicate(
     71                                        out, _ = proc.communicate(
    7372                                                timeout = settings.timeout.single if timeout else None
    7473                                        )
    7574
    76                                         return proc.returncode, out.decode("latin-1") if out else None, errout.decode("latin-1") if errout else None
     75                                        return proc.returncode, out.decode("latin-1") if out else None
    7776                                except subprocess.TimeoutExpired:
    7877                                        if settings.timeout2gdb:
    7978                                                print("Process {} timeout".format(proc.pid))
    8079                                                proc.communicate()
    81                                                 return 124, str(None), "Subprocess Timeout 2 gdb"
     80                                                return 124, str(None)
    8281                                        else:
    8382                                                proc.send_signal(signal.SIGABRT)
    8483                                                proc.communicate()
    85                                                 return 124, str(None), "Subprocess Timeout 2 gdb"
     84                                                return 124, str(None)
    8685
    8786        except Exception as ex:
     
    106105                return (False, "No file")
    107106
    108         code, out, err = sh("file", fname, output_file=subprocess.PIPE)
     107        code, out = sh("file", fname, output_file=subprocess.PIPE)
    109108        if code != 0:
    110                 return (False, "'file EXPECT' failed with code {} '{}'".format(code, err))
     109                return (False, "'file EXPECT' failed with code {}".format(code))
    111110
    112111        match = re.search(".*: (.*)", out)
     
    191190        ]
    192191        cmd = [s for s in cmd if s]
    193         return sh(*cmd, output_file=output_file, error=error, pass_fds=settings.make_jobfds)
     192        return sh(*cmd, output_file=output_file, error=error)
    194193
    195194def make_recon(target):
     
    242241# move a file
    243242def mv(source, dest):
    244         ret, _, _ = sh("mv", source, dest)
     243        ret, _ = sh("mv", source, dest)
    245244        return ret
    246245
    247246# cat one file into the other
    248247def cat(source, dest):
    249         ret, _, _ = sh("cat", source, output_file=dest)
     248        ret, _ = sh("cat", source, output_file=dest)
    250249        return ret
    251250
     
    290289#               system
    291290################################################################################
    292 def jobserver_version():
    293         make_ret, out, err = sh('make', '.test_makeflags', '-j2', output_file=subprocess.PIPE, error=subprocess.PIPE)
    294         if make_ret != 0:
    295                 print("ERROR: cannot find Makefile jobserver version", file=sys.stderr)
    296                 print("       test returned : {} '{}'".format(make_ret, err), file=sys.stderr)
     291# count number of jobs to create
     292def job_count( options, tests ):
     293        # check if the user already passed in a number of jobs for multi-threading
     294        if not options.jobs:
     295                make_flags = os.environ.get('MAKEFLAGS')
     296                force = bool(make_flags)
     297                make_jobs_fds = re.search("--jobserver-(auth|fds)=\s*([0-9]+),([0-9]+)", make_flags) if make_flags else None
     298                if make_jobs_fds :
     299                        tokens = os.read(int(make_jobs_fds.group(2)), 1024)
     300                        options.jobs = len(tokens)
     301                        os.write(int(make_jobs_fds.group(3)), tokens)
     302                else :
     303                        if settings.distribute:
     304                                ret, jstr = sh("distcc", "-j", output_file=subprocess.PIPE, ignore_dry_run=True)
     305                                if ret == 0:
     306                                        options.jobs = int(jstr.strip())
     307                                else :
     308                                        options.jobs = multiprocessing.cpu_count()
     309                        else:
     310                                options.jobs = multiprocessing.cpu_count()
     311        else :
     312                force = True
     313
     314        # make sure we have a valid number of jobs that corresponds to user input
     315        if options.jobs <= 0 :
     316                print('ERROR: Invalid number of jobs', file=sys.stderr)
    297317                sys.exit(1)
    298318
    299         re_jobs = re.search("--jobserver-(auth|fds)", out)
    300         if not re_jobs:
    301                 print("ERROR: cannot find Makefile jobserver version", file=sys.stderr)
    302                 print("       MAKEFLAGS are : '{}'".format(out), file=sys.stderr)
    303                 sys.exit(1)
    304 
    305         return "--jobserver-{}".format(re_jobs.group(1))
    306 
    307 def prep_recursive_make(N):
    308         if N < 2:
    309                 return []
    310 
    311         # create the pipe
    312         (r, w) = os.pipe()
    313 
    314         # feel it with N-1 tokens, (Why N-1 and not N, I don't know it's in the manpage for make)
    315         os.write(w, b'+' * (N - 1));
    316 
    317         # prep the flags for make
    318         make_flags = ["-j{}".format(N), "--jobserver-auth={},{}".format(r, w)]
    319 
    320         # tell make about the pipes
    321         os.environ["MAKEFLAGS"] = os.environ["MFLAGS"] = " ".join(make_flags)
    322 
    323         # make sure pass the pipes to our children
    324         settings.update_make_fds(r, w)
    325 
    326         return make_flags
    327 
    328 def prep_unlimited_recursive_make():
    329         # prep the flags for make
    330         make_flags = ["-j"]
    331 
    332         # tell make about the pipes
    333         os.environ["MAKEFLAGS"] = os.environ["MFLAGS"] = "-j"
    334 
    335         return make_flags
    336 
    337 
    338 def eval_hardware():
    339         # we can create as many things as we want
    340         # how much hardware do we have?
    341         if settings.distribute:
    342                 # remote hardware is allowed
    343                 # how much do we have?
    344                 ret, jstr, _ = sh("distcc", "-j", output_file=subprocess.PIPE, ignore_dry_run=True)
    345                 return int(jstr.strip()) if ret == 0 else multiprocessing.cpu_count()
    346         else:
    347                 # remote isn't allowed, use local cpus
    348                 return multiprocessing.cpu_count()
    349 
    350 # count number of jobs to create
    351 def job_count( options ):
    352         # check if the user already passed in a number of jobs for multi-threading
    353         make_env = os.environ.get('MAKEFLAGS')
    354         make_flags = make_env.split() if make_env else None
    355         jobstr = jobserver_version()
    356 
    357         if options.jobs and make_flags:
    358                 print('WARNING: -j options should not be specified when called form Make', file=sys.stderr)
    359 
    360         # Top level make is calling the shots, just follow
    361         if make_flags:
    362                 # do we have -j and --jobserver-...
    363                 jobopt = None
    364                 exists_fds = None
    365                 for f in make_flags:
    366                         jobopt = f if f.startswith("-j") else jobopt
    367                         exists_fds = f if f.startswith(jobstr) else exists_fds
    368 
    369                 # do we have limited parallelism?
    370                 if exists_fds :
    371                         try:
    372                                 rfd, wfd = tuple(exists_fds.split('=')[1].split(','))
    373                         except:
    374                                 print("ERROR: jobserver has unrecoginzable format, was '{}'".format(exists_fds), file=sys.stderr)
    375                                 sys.exit(1)
    376 
    377                         # read the token pipe to count number of available tokens and restore the pipe
    378                         # this assumes the test suite script isn't invoked in parellel with something else
    379                         tokens = os.read(int(rfd), 65536)
    380                         os.write(int(wfd), tokens)
    381 
    382                         # the number of tokens is off by one for obscure but well documented reason
    383                         # see man make for more details
    384                         options.jobs = len(tokens) + 1
    385 
    386                 # do we have unlimited parallelism?
    387                 elif jobopt and jobopt != "-j1":
    388                         # check that this actually make sense
    389                         if jobopt != "-j":
    390                                 print("ERROR: -j option passed by make but no {}, was '{}'".format(jobstr, jobopt), file=sys.stderr)
    391                                 sys.exit(1)
    392 
    393                         options.jobs = eval_hardware()
    394                         flags = prep_unlimited_recursive_make()
    395 
    396 
    397                 # then no parallelism
    398                 else:
    399                         options.jobs = 1
    400 
    401                 # keep all flags make passed along, except the weird 'w' which is about subdirectories
    402                 flags = [f for f in make_flags if f != 'w']
    403 
    404         # Arguments are calling the shots, fake the top level make
    405         elif options.jobs :
    406 
    407                 # make sure we have a valid number of jobs that corresponds to user input
    408                 if options.jobs < 0 :
    409                         print('ERROR: Invalid number of jobs', file=sys.stderr)
    410                         sys.exit(1)
    411 
    412                 flags = prep_recursive_make(options.jobs)
    413 
    414         # Arguments are calling the shots, fake the top level make, but 0 is a special case
    415         elif options.jobs == 0:
    416                 options.jobs = eval_hardware()
    417                 flags = prep_unlimited_recursive_make()
    418 
    419         # No one says to run in parallel, then don't
    420         else :
    421                 options.jobs = 1
    422                 flags = []
    423 
    424         # Make sure we call make as expected
    425         settings.update_make_cmd( flags )
    426 
    427         # return the job count
    428         return options.jobs
     319        return min( options.jobs, len(tests) ), force
    429320
    430321# enable core dumps for all the test children
     
    443334        distcc_hash = os.path.join(settings.SRCDIR, '../tools/build/distcc_hash')
    444335        config = "%s-%s" % (settings.arch.target, settings.debug.path)
    445         _, out, _ = sh(distcc_hash, config, output_file=subprocess.PIPE, ignore_dry_run=True)
     336        _, out = sh(distcc_hash, config, output_file=subprocess.PIPE, ignore_dry_run=True)
    446337        return out.strip()
    447338
     
    483374
    484375        if not os.path.isfile(core):
    485                 return 1, "ERR No core dump, expected '{}' (limit soft: {} hard: {})".format(core, *resource.getrlimit(resource.RLIMIT_CORE))
     376                return 1, "ERR No core dump (limit soft: {} hard: {})".format(*resource.getrlimit(resource.RLIMIT_CORE))
    486377
    487378        try:
    488                 ret, out, err = sh('gdb', '-n', path, core, '-batch', '-x', cmd, output_file=subprocess.PIPE)
    489                 if ret == 0:
    490                         return 0, out
    491                 else:
    492                         return 1, err
     379                return sh('gdb', '-n', path, core, '-batch', '-x', cmd, output_file=subprocess.PIPE)
    493380        except:
    494381                return 1, "ERR Could not read core with gdb"
  • tests/test.py

    r92538ab r4559b34  
    140140        parser.add_argument('--regenerate-expected', help='Regenerate the .expect by running the specified tets, can be used with --all option', action='store_true')
    141141        parser.add_argument('--archive-errors', help='If called with a valid path, on test crashes the test script will copy the core dump and the executable to the specified path.', type=str, default='')
    142         parser.add_argument('-j', '--jobs', help='Number of tests to run simultaneously, 0 (default) for unlimited', nargs='?', const=0, type=int)
     142        parser.add_argument('-j', '--jobs', help='Number of tests to run simultaneously', type=int)
    143143        parser.add_argument('--list-comp', help='List all valide arguments', action='store_true')
    144144        parser.add_argument('--list-dist', help='List all tests for distribution', action='store_true')
     
    195195        # build, skipping to next test on error
    196196        with Timed() as comp_dur:
    197                 make_ret, _, _ = make( test.target(), output_file=subprocess.DEVNULL, error=out_file, error_file = err_file )
     197                make_ret, _ = make( test.target(), output_file=subprocess.DEVNULL, error=out_file, error_file = err_file )
    198198
    199199        # ----------
     
    208208                                if settings.dry_run or is_exe(exe_file):
    209209                                        # run test
    210                                         retcode, _, _ = sh(exe_file, output_file=out_file, input_file=in_file, timeout=True)
     210                                        retcode, _ = sh(exe_file, output_file=out_file, input_file=in_file, timeout=True)
    211211                                else :
    212212                                        # simply cat the result into the output
     
    226226                        else :
    227227                                # fetch return code and error from the diff command
    228                                 retcode, error, _ = diff(cmp_file, out_file)
     228                                retcode, error = diff(cmp_file, out_file)
    229229
    230230                else:
     
    366366                        print(os.path.relpath(t.expect(), settings.SRCDIR), end=' ')
    367367                        print(os.path.relpath(t.input() , settings.SRCDIR), end=' ')
    368                         code, out, err = make_recon(t.target())
     368                        code, out = make_recon(t.target())
    369369
    370370                        if code != 0:
    371                                 print('ERROR: recond failed for test {}: {} \'{}\''.format(t.target(), code, err), file=sys.stderr)
     371                                print('ERROR: recond failed for test {}'.format(t.target()), file=sys.stderr)
    372372                                sys.exit(1)
    373373
     
    417417                        if is_empty(t.expect()):
    418418                                print('WARNING: test "{}" has empty .expect file'.format(t.target()), file=sys.stderr)
    419 
    420         options.jobs = job_count( options )
    421419
    422420        # for each build configurations, run the test
     
    432430                        local_tests = settings.ast.filter( tests )
    433431                        local_tests = settings.arch.filter( local_tests )
     432                        options.jobs, forceJobs = job_count( options, local_tests )
     433                        settings.update_make_cmd(forceJobs, options.jobs)
    434434
    435435                        # check the build configuration works
    436436                        settings.validate()
    437                         jobs = min(options.jobs, len(local_tests))
    438437
    439438                        # print configuration
     
    441440                                'Regenerating' if settings.generating else 'Running',
    442441                                len(local_tests),
    443                                 jobs,
     442                                options.jobs,
    444443                                settings.ast.string,
    445444                                settings.arch.string,
     
    451450
    452451                        # otherwise run all tests and make sure to return the correct error code
    453                         failed = run_tests(local_tests, jobs)
     452                        failed = run_tests(local_tests, options.jobs)
    454453                        if failed:
    455454                                if not settings.continue_:
Note: See TracChangeset for help on using the changeset viewer.