Changes in / [3c64c668:58fe85a]


Ignore:
Files:
535 added
182 deleted
359 edited

Legend:

Unmodified
Added
Removed
  • .gitignore

    r3c64c668 r58fe85a  
    44
    55# generated by configure
     6aclocal.m4
     7automake
    68autom4te.cache
    79config.h
     
    911config.log
    1012config.py
     13configure
     14libtool
    1115stamp-h1
    12 libtool
    1316/Makefile
     17/Makefile.in
    1418**/Makefile
     19**/Makefile.in
     20**/Makefile.dist.in
    1521/version
    1622
     
    4248libcfa/x64-debug/
    4349libcfa/x64-nodebug/
    44 libcfa/x64-nolib/
    4550libcfa/x86-debug/
    4651libcfa/x86-nodebug/
    47 libcfa/x86-nolib/
    48 libcfa/arm-debug/
    49 libcfa/arm-nodebug/
    50 libcfa/arm-nolib/
     52libcfa/arm64-debug/
     53libcfa/arm64-nodebug/
    5154
    5255# generated by bison and lex from parser.yy and lex.ll
     
    7376doc/user/pointer2.tex
    7477doc/user/EHMHierarchy.tex
     78
     79# generated by npm
     80package-lock.json
  • Jenkins/FullBuild

    r3c64c668 r58fe85a  
    88        def err = null
    99
     10        final scmVars = checkout scm
     11        final commitId = scmVars.GIT_COMMIT
     12
    1013        try {
    1114                //Wrap build to add timestamp to command line
     
    1417                        stage('Build') {
    1518
    16                                 results = [null, null]
     19                                parallel (
     20                                        gcc_8_x86_old: { trigger_build( 'gcc-8',   'x86', false ) },
     21                                        gcc_7_x86_old: { trigger_build( 'gcc-7',   'x86', false ) },
     22                                        gcc_6_x86_old: { trigger_build( 'gcc-6',   'x86', false ) },
     23                                        gcc_9_x64_old: { trigger_build( 'gcc-9',   'x64', false ) },
     24                                        gcc_8_x64_old: { trigger_build( 'gcc-8',   'x64', false ) },
     25                                        gcc_7_x64_old: { trigger_build( 'gcc-7',   'x64', false ) },
     26                                        gcc_6_x64_old: { trigger_build( 'gcc-6',   'x64', false ) },
     27                                        gcc_5_x64_old: { trigger_build( 'gcc-5',   'x64', false ) },
     28                                        clang_x64_old: { trigger_build( 'clang',   'x64', false ) },
     29                                        clang_x64_new: { trigger_build( 'clang',   'x64', true  ) },
     30                                )
     31                        }
    1732
    18                                 parallel (
    19                                         clang_x86: { trigger_build( 'gcc-8',   'x86' ) },
    20                                         gcc_5_x86: { trigger_build( 'gcc-7',   'x86' ) },
    21                                         gcc_6_x86: { trigger_build( 'gcc-6',   'x86' ) },
    22                                         gcc_9_x64: { trigger_build( 'gcc-9',   'x64' ) },
    23                                         gcc_8_x64: { trigger_build( 'gcc-8',   'x64' ) },
    24                                         gcc_7_x64: { trigger_build( 'gcc-7',   'x64' ) },
    25                                         gcc_6_x64: { trigger_build( 'gcc-6',   'x64' ) },
    26                                         gcc_5_x64: { trigger_build( 'gcc-5',   'x64' ) },
    27                                         clang_x64: { trigger_build( 'clang',   'x64' ) },
    28                                 )
     33                        stage('Package') {
     34                                trigger_dist( commitId, currentBuild.number.toString() )
    2935                        }
    3036                }
     
    5965//===========================================================================================================
    6066
    61 def trigger_build(String cc, String arch) {
     67def trigger_build(String cc, String arch, boolean new_ast) {
    6268        def result = build job: 'Cforall/master',               \
    6369                parameters: [                                           \
     
    6874                          name: 'Architecture',                         \
    6975                          value: arch],                                 \
     76                        [$class: 'BooleanParameterValue',               \
     77                          name: 'NewAST',                               \
     78                          value: new_ast],                              \
    7079                        [$class: 'BooleanParameterValue',               \
    7180                          name: 'RunAllTests',                          \
     
    7988                        [$class: 'BooleanParameterValue',               \
    8089                          name: 'Publish',                              \
    81                           value: true],                                 \
     90                          value: true],                                         \
    8291                        [$class: 'BooleanParameterValue',               \
    8392                          name: 'Silent',                               \
     
    94103}
    95104
    96 //Helper routine to collect information about the git history
    97 def collect_git_info() {
     105def trigger_dist(String commitId, String buildNum) {
     106        def result = build job: 'Cforall_Distribute_Ref',       \
     107                parameters: [                                           \
     108                        string(name: 'GitRef', value: commitId),        \
     109                        string(name: 'Build' , value: buildNum) \
     110                ],                                                              \
     111                propagate: false
    98112
    99         //create the temporary output directory in case it doesn't already exist
    100         def out_dir = pwd tmp: true
    101         sh "mkdir -p ${out_dir}"
     113        echo(result.result)
    102114
    103         //parse git logs to find what changed
    104         dir("../Cforall_Full_Build@script") {
    105                 sh "git reflog > ${out_dir}/GIT_COMMIT"
     115        if(result.result != 'SUCCESS') {
     116                sh("wget -q -O - https://cforall.uwaterloo.ca/jenkins/job/Cforall_Distribute_Ref/${result.number}/consoleText")
     117                error(result.result)
    106118        }
    107         git_reflog = readFile("${out_dir}/GIT_COMMIT")
    108         gitRefOldValue = (git_reflog =~ /moving from (.+) to (.+)/)[0][1]
    109         gitRefNewValue = (git_reflog =~ /moving from (.+) to (.+)/)[0][2]
    110119}
    111120
  • Jenkinsfile

    r3c64c668 r58fe85a  
    22
    33import groovy.transform.Field
    4 
    5 // For skipping stages
    6 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
    74
    85//===========================================================================================================
     
    1512        SrcDir    = pwd tmp: false
    1613        Settings  = null
    17         StageName = ''
     14        Tools     = null
    1815
    1916        // Local variables
     
    3330                                SrcDir    = pwd tmp: false
    3431
    35                                 clean()
    36 
    37                                 checkout()
     32                                Tools.Clean()
     33
     34                                Tools.Checkout()
    3835
    3936                                build()
     
    5754        //attach the build log to the email
    5855        catch (Exception caughtError) {
    59                 //rethrow error later
     56                // Store the result of the build log
     57                currentBuild.result = "FAILURE"
     58
     59                // An error has occured, the build log is relevent
     60                log_needed = true
     61
     62                // rethrow error later
    6063                err = caughtError
    6164
     65                // print the error so it shows in the log
    6266                echo err.toString()
    63 
    64                 //An error has occured, the build log is relevent
    65                 log_needed = true
    66 
    67                 //Store the result of the build log
    68                 currentBuild.result = "${StageName} FAILURE".trim()
    6967        }
    7068
     
    8482// Main compilation routines
    8583//===========================================================================================================
    86 def clean() {
    87         build_stage('Cleanup', true) {
    88                 // clean the build by wipping the build directory
    89                 dir(BuildDir) {
    90                         deleteDir()
    91                 }
    92         }
    93 }
    94 
    95 //Compilation script is done here but environnement set-up and error handling is done in main loop
    96 def checkout() {
    97         build_stage('Checkout', true) {
    98                 //checkout the source code and clean the repo
    99                 final scmVars = checkout scm
    100                 Settings.GitNewRef = scmVars.GIT_COMMIT
    101                 Settings.GitOldRef = scmVars.GIT_PREVIOUS_COMMIT
    102 
    103                 echo GitLogMessage()
    104 
    105                 // This is a complete hack but it solves problems with automake thinking it needs to regenerate makefiles
    106                 // We fudged automake/missing to handle that but automake stills bakes prints inside the makefiles
    107                 // and these cause more problems.
    108                 sh 'find . -name Makefile.in -exec touch {} +'
    109         }
    110 }
    111 
    11284def build() {
    11385        debug = true
    11486        release = Settings.RunAllTests || Settings.RunBenchmark
    115         build_stage('Build : configure', true) {
     87        Tools.BuildStage('Build : configure', true) {
     88                // Configure must be run inside the tree
     89                dir (SrcDir) {
     90                        // Generate the necessary build files
     91                        sh './autogen.sh'
     92                }
     93
    11694                // Build outside of the src tree to ease cleaning
    11795                dir (BuildDir) {
    118                         //Configure the conpilation (Output is not relevant)
     96                        //Configure the compilation (Output is not relevant)
    11997                        //Use the current directory as the installation target so nothing escapes the sandbox
    12098                        //Also specify the compiler by hand
     
    126104                        }
    127105
    128                         sh "${SrcDir}/configure CXX=${Settings.Compiler.CXX} CC=${Settings.Compiler.CC} ${Settings.Architecture.flags} ${targets} --quiet"
     106                        ast = Settings.NewAST ? "--enable-new-ast" : "--disable-new-ast"
     107
     108                        sh "${SrcDir}/configure CXX=${Settings.Compiler.CXX} CC=${Settings.Compiler.CC} ${Settings.Architecture.flags} AR=gcc-ar RANLIB=gcc-ranlib ${targets} ${ast} --quiet --prefix=${BuildDir}"
    129109
    130110                        // Configure libcfa
     
    133113        }
    134114
    135         build_stage('Build : cfa-cpp', true) {
     115        Tools.BuildStage('Build : cfa-cpp', true) {
    136116                // Build outside of the src tree to ease cleaning
    137117                dir (BuildDir) {
     
    144124        }
    145125
    146         build_stage('Build : libcfa(debug)', debug) {
     126        Tools.BuildStage('Build : libcfa(debug)', debug) {
    147127                // Build outside of the src tree to ease cleaning
    148128                dir (BuildDir) {
     
    151131        }
    152132
    153         build_stage('Build : libcfa(nodebug)', release) {
     133        Tools.BuildStage('Build : libcfa(nodebug)', release) {
    154134                // Build outside of the src tree to ease cleaning
    155135                dir (BuildDir) {
    156136                        sh "make -j 8 --no-print-directory -C libcfa/${Settings.Architecture.name}-nodebug"
     137                }
     138        }
     139
     140        Tools.BuildStage('Build : install', true) {
     141                // Build outside of the src tree to ease cleaning
     142                dir (BuildDir) {
     143                        sh "make -j 8 --no-print-directory install"
    157144                }
    158145        }
     
    161148def test() {
    162149        try {
    163                 build_stage('Test: short', !Settings.RunAllTests) {
     150                Tools.BuildStage('Test: short', !Settings.RunAllTests) {
    164151                        dir (BuildDir) {
    165152                                //Run the tests from the tests directory
     
    168155                }
    169156
    170                 build_stage('Test: full', Settings.RunAllTests) {
     157                Tools.BuildStage('Test: full', Settings.RunAllTests) {
    171158                        dir (BuildDir) {
    172159                                        //Run the tests from the tests directory
     
    179166                echo "Archiving core dumps"
    180167                dir (BuildDir) {
    181                         archiveArtifacts artifacts: "tests/crashes/**/*", fingerprint: true
     168                        archiveArtifacts artifacts: "tests/crashes/**/*,lib/**/lib*.so*", fingerprint: true
    182169                }
    183170                throw err
     
    186173
    187174def benchmark() {
    188         build_stage('Benchmark', Settings.RunBenchmark) {
     175        Tools.BuildStage('Benchmark', Settings.RunBenchmark) {
    189176                dir (BuildDir) {
    190177                        //Append bench results
     
    195182
    196183def build_doc() {
    197         build_stage('Documentation', Settings.BuildDocumentation) {
     184        Tools.BuildStage('Documentation', Settings.BuildDocumentation) {
    198185                dir ('doc/user') {
    199186                        make_doc()
     
    207194
    208195def publish() {
    209         build_stage('Publish', true) {
     196        Tools.BuildStage('Publish', true) {
    210197
    211198                if( Settings.Publish && !Settings.RunBenchmark ) { echo 'No results to publish!!!' }
     
    229216//Routine responsible of sending the email notification once the build is completed
    230217//===========================================================================================================
    231 @NonCPS
    232 def SplitLines(String text) {
    233         def list = []
    234 
    235         text.eachLine {
    236                 list += it
    237         }
    238 
    239         return list
    240 }
    241 
    242 def GitLogMessage() {
    243         if (!Settings || !Settings.GitOldRef || !Settings.GitNewRef) return "\nERROR retrieveing git information!\n"
    244 
    245         def oldRef = Settings.GitOldRef
    246         def newRef = Settings.GitNewRef
    247 
    248         def revText = sh(returnStdout: true, script: "git rev-list ${oldRef}..${newRef}").trim()
    249         def revList = SplitLines( revText )
    250 
    251         def gitUpdate = ""
    252         revList.each { rev ->
    253                 def type = sh(returnStdout: true, script: "git cat-file -t ${rev}").trim()
    254                 gitUpdate = gitUpdate + "       via  ${rev} (${type})"
    255         }
    256 
    257         def rev = oldRef
    258         def type = sh(returnStdout: true, script: "git cat-file -t ${rev}").trim()
    259         gitUpdate = gitUpdate + "      from  ${rev} (${type})"
    260 
    261         def gitLog    = sh(returnStdout: true, script: "git rev-list --format=short ${oldRef}...${newRef}").trim()
    262 
    263         def gitDiff   = sh(returnStdout: true, script: "git diff --stat --color ${newRef} ${oldRef}").trim()
    264         gitDiff = gitDiff.replace('[32m', '<span style="color: #00AA00;">')
    265         gitDiff = gitDiff.replace('[31m', '<span style="color: #AA0000;">')
    266         gitDiff = gitDiff.replace('[m', '</span>')
    267 
    268         return """
    269 <pre>
    270 The branch ${env.BRANCH_NAME} has been updated.
    271 ${gitUpdate}
    272 </pre>
    273 
    274 <p>Check console output at ${env.BUILD_URL} to view the results.</p>
    275 
    276 <p>- Status --------------------------------------------------------------</p>
    277 
    278 <p>BUILD# ${env.BUILD_NUMBER} - ${currentBuild.result}</p>
    279 
    280 <p>- Log -----------------------------------------------------------------</p>
    281 
    282 <pre>
    283 ${gitLog}
    284 </pre>
    285 
    286 <p>-----------------------------------------------------------------------</p>
    287 <pre>
    288 Summary of changes:
    289 ${gitDiff}
    290 </pre>
    291 """
    292 }
    293 
    294218//Standard build email notification
    295219def email(boolean log) {
     
    303227generated because of a git hooks/post-receive script following
    304228a ref change which was pushed to the C\u2200 repository.</p>
    305 """ + GitLogMessage()
     229""" + Tools.GitLogMessage()
    306230
    307231        def email_to = !Settings.IsSandbox ? "cforall@lists.uwaterloo.ca" : "tdelisle@uwaterloo.ca"
     
    325249        public String CXX
    326250        public String CC
    327 
    328         CC_Desc(String name, String CXX, String CC) {
     251        public String lto
     252
     253        CC_Desc(String name, String CXX, String CC, String lto) {
    329254                this.name = name
    330255                this.CXX = CXX
    331                 this.CC = CC
     256                this.CC  = CC
     257                this.lto = lto
    332258        }
    333259}
     
    349275        public final CC_Desc Compiler
    350276        public final Arch_Desc Architecture
     277        public final Boolean NewAST
    351278        public final Boolean RunAllTests
    352279        public final Boolean RunBenchmark
     
    364291                switch( param.Compiler ) {
    365292                        case 'gcc-9':
    366                                 this.Compiler = new CC_Desc('gcc-9', 'g++-9', 'gcc-9')
     293                                this.Compiler = new CC_Desc('gcc-9', 'g++-9', 'gcc-9', '-flto=auto')
    367294                        break
    368295                        case 'gcc-8':
    369                                 this.Compiler = new CC_Desc('gcc-8', 'g++-8', 'gcc-8')
     296                                this.Compiler = new CC_Desc('gcc-8', 'g++-8', 'gcc-8', '-flto=auto')
    370297                        break
    371298                        case 'gcc-7':
    372                                 this.Compiler = new CC_Desc('gcc-7', 'g++-7', 'gcc-7')
     299                                this.Compiler = new CC_Desc('gcc-7', 'g++-7', 'gcc-7', '-flto=auto')
    373300                        break
    374301                        case 'gcc-6':
    375                                 this.Compiler = new CC_Desc('gcc-6', 'g++-6', 'gcc-6')
     302                                this.Compiler = new CC_Desc('gcc-6', 'g++-6', 'gcc-6', '-flto=auto')
    376303                        break
    377304                        case 'gcc-5':
    378                                 this.Compiler = new CC_Desc('gcc-5', 'g++-5', 'gcc-5')
     305                                this.Compiler = new CC_Desc('gcc-5', 'g++-5', 'gcc-5', '-flto=auto')
    379306                        break
    380307                        case 'gcc-4.9':
    381                                 this.Compiler = new CC_Desc('gcc-4.9', 'g++-4.9', 'gcc-4.9')
     308                                this.Compiler = new CC_Desc('gcc-4.9', 'g++-4.9', 'gcc-4.9', '-flto=auto')
    382309                        break
    383310                        case 'clang':
    384                                 this.Compiler = new CC_Desc('clang', 'clang++-6.0', 'gcc-6')
     311                                this.Compiler = new CC_Desc('clang', 'clang++-10', 'gcc-9', '-flto=thin -flto-jobs=0')
    385312                        break
    386313                        default :
     
    400327
    401328                this.IsSandbox          = (branch == "jenkins-sandbox")
     329                this.NewAST             = param.NewAST
    402330                this.RunAllTests        = param.RunAllTests
    403331                this.RunBenchmark       = param.RunBenchmark
     
    409337                this.DescShort = "${ this.Compiler.name }:${ this.Architecture.name }${full}"
    410338
     339                final ast = this.NewAST ? "New AST" : "Old AST"
    411340                this.DescLong = """Compiler              : ${ this.Compiler.name } (${ this.Compiler.CXX }/${ this.Compiler.CC })
     341AST Version             : ${ ast.toString() }
    412342Architecture            : ${ this.Architecture.name }
    413343Arc Flags               : ${ this.Architecture.flags }
     
    439369        // prepare the properties
    440370        properties ([                                                                                                   \
     371                buildDiscarder(logRotator(                                                                              \
     372                        artifactDaysToKeepStr: '',                                                                      \
     373                        artifactNumToKeepStr: '',                                                                       \
     374                        daysToKeepStr: '730',                                                                           \
     375                        numToKeepStr: '1000'                                                                            \
     376                )),                                                                                                             \
    441377                [$class: 'ParametersDefinitionProperty',                                                                \
    442378                        parameterDefinitions: [                                                                         \
     
    444380                                        description: 'Which compiler to use',                                   \
    445381                                        name: 'Compiler',                                                                       \
    446                                         choices: 'gcc-9\ngcc-8\ngcc-7\ngcc-6\ngcc-5\ngcc-4.9\nclang',                                   \
     382                                        choices: 'gcc-9\ngcc-8\ngcc-7\ngcc-6\ngcc-5\ngcc-4.9\nclang',   \
    447383                                        defaultValue: 'gcc-8',                                                          \
    448384                                ],                                                                                              \
     
    454390                                ],                                                                                              \
    455391                                [$class: 'BooleanParameterDefinition',                                                  \
     392                                        description: 'If true, build compiler using new AST',           \
     393                                        name: 'NewAST',                                                                         \
     394                                        defaultValue: true,                                                             \
     395                                ],                                                                                              \
     396                                [$class: 'BooleanParameterDefinition',                                                  \
    456397                                        description: 'If false, only the quick test suite is ran',              \
    457398                                        name: 'RunAllTests',                                                            \
     
    481422                ]])
    482423
    483         // It's unfortunate but it looks like we need to checkout the entire repo just to get the pretty git printer
     424        // It's unfortunate but it looks like we need to checkout the entire repo just to get
     425        // - the pretty git printer
     426        // - Jenkins.tools
    484427        checkout scm
     428
     429        Tools = load "Jenkins/tools.groovy"
    485430
    486431        final settings = new BuildSettings(params, env.BRANCH_NAME)
     
    490435
    491436        return settings
    492 }
    493 
    494 def build_stage(String name, boolean run, Closure block ) {
    495         StageName = name
    496         echo " -------- ${StageName} -------- "
    497         if(run) {
    498                 stage(name, block)
    499         } else {
    500                 stage(name) { Utils.markStageSkippedForConditional(STAGE_NAME) }
    501         }
    502437}
    503438
  • Makefile.am

    r3c64c668 r58fe85a  
    1919
    2020MAINTAINERCLEANFILES = lib/* bin/* tests/.deps/* tests/.out/* # order important
     21DISTCLEANFILES = version
    2122
    2223SUBDIRS = driver src . @LIBCFA_TARGET_DIRS@
     24DIST_SUBDIRS = driver src . libcfa tests
    2325
    2426@LIBCFA_TARGET_MAKEFILES@ : Makefile $(srcdir)/libcfa/configure
     
    2628        @ls $(config_file) || (echo "Missing config.data, re-run configure script again" && false)
    2729        @$(eval config_data = $(shell cat $(config_file)))
    28         @echo "Configuring libcfa with '$(config_data)''"
     30        @echo "Configuring libcfa ($(abs_top_srcdir)/libcfa/configure) with '$(config_data)' from $(shell pwd) / $(dir $@)"
    2931        @cd $(dir $@) && $(abs_top_srcdir)/libcfa/configure $(config_data)
    3032
     
    3234
    3335man1_MANS = doc/man/cfa.1
     36
     37EXTRA_DIST = LICENSE doc/man/cfa.1 libcfa/configure libcfa/Makefile.dist.am libcfa/Makefile.dist.in tools/build/distcc_hash tools/build/push2dist.sh
    3438
    3539debug=yes
     
    4751        @./config.status --config | sed "s/ /\n\t/g; s/\t'/\t/g; s/'\n/\n/g; s/^'//g; s/'$$//g"
    4852        @find libcfa -name config.status -printf "\n%h\n\t" -exec {} --config \; | sed "s/ /\n\t/g; s/\t'/\t/g; s/'\n/\n/g; s/^'//g; s/'$$//g"
     53
     54mostlyclean-local: @LIBCFA_TARGET_MAKEFILES@
     55        for dir in @LIBCFA_TARGET_DIRS@; do \
     56                $(MAKE) -C $${dir} mostlyclean; \
     57        done
     58
     59clean-local: @LIBCFA_TARGET_MAKEFILES@
     60        for dir in @LIBCFA_TARGET_DIRS@; do \
     61                $(MAKE) -C $${dir} clean; \
     62        done
     63
     64distclean-local: @LIBCFA_TARGET_MAKEFILES@
     65        for dir in @LIBCFA_TARGET_DIRS@; do \
     66                $(MAKE) -C $${dir} distclean; \
     67                rm $${dir}/config.data; \
     68        done
  • benchmark/Makefile.am

    r3c64c668 r58fe85a  
    1111## Created On       : Sun May 31 09:08:15 2015
    1212## Last Modified By : Peter A. Buhr
    13 ## Last Modified On : Sat Jan 25 09:20:44 2020
    14 ## Update Count     : 255
     13## Last Modified On : Tue Mar 10 11:41:18 2020
     14## Update Count     : 258
    1515###############################################################################
    1616
     
    1919
    2020# applies to both programs
    21 include $(top_srcdir)/src/cfa.make
     21include $(top_srcdir)/tools/build/cfa.make
    2222
    2323AM_CFLAGS = -O2 -Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror
     
    6666# Dummy hack tricks
    6767EXTRA_PROGRAMS = dummy # build but do not install
    68 dummy_SOURCES = dummyC.c dummyCXX.cpp
     68nodist_dummy_SOURCES = dummyC.c dummyCXX.cpp
    6969
    7070dummyC.c:
     
    8080## =========================================================================================================
    8181
    82 all : basic$(EXEEXT) ctxswitch$(EXEEXT) mutex$(EXEEXT) schedint$(EXEEXT) schedext$(EXEEXT) creation$(EXEEXT)
     82# all is used by make dist so ignore it
     83all:
     84
     85all-bench : basic$(EXEEXT) ctxswitch$(EXEEXT) mutex$(EXEEXT) schedint$(EXEEXT) schedext$(EXEEXT) creation$(EXEEXT)
    8386
    8487basic_loop_DURATION = 15000000000
     
    108111creation_cfa_coroutine_DURATION = 100000000
    109112creation_cfa_coroutine_eager_DURATION = 10000000
     113creation_cfa_generator_DURATION = 1000000000
    110114creation_upp_coroutine_DURATION = ${creation_cfa_coroutine_eager_DURATION}
    111 creation_cfa_thread_DURATION = 10000000
    112 creation_upp_thread_DURATION = ${creation_cfa_thread_DURATION}
    113115creation_DURATION = 10000000
    114116
     
    144146
    145147cleancsv:
    146         rm -f compile.csv basic.csv ctxswitch.csv mutex.csv scheduling.csv
     148        rm -f compile.csv basic.csv ctxswitch.csv mutex.csv schedint.csv
    147149
    148150jenkins$(EXEEXT): cleancsv
     
    155157        +make mutex.csv
    156158        -+make mutex.diff.csv
    157         +make scheduling.csv
    158         -+make scheduling.diff.csv
     159        +make schedint.csv
     160        -+make schedint.diff.csv
    159161@DOifskipcompile@
    160162        cat compile.csv
     
    165167        cat mutex.csv
    166168        -cat mutex.diff.csv
    167         cat scheduling.csv
    168         -cat scheduling.diff.csv
     169        cat schedint.csv
     170        -cat schedint.diff.csv
    169171
    170172compile.csv:
     
    196198        $(srcdir)/fixcsv.sh $@
    197199
    198 scheduling.csv:
     200schedint.csv:
    199201        echo "building $@"
    200202        echo "schedint-1,schedint-2,schedext-1,schedext-2" > $@
     
    287289ctxswitch-python_coroutine$(EXEEXT):
    288290        $(BENCH_V_PY)echo "#!/bin/sh" > a.out
    289         echo "python3.7 $(srcdir)/ctxswitch/python_cor.py" >> a.out
     291        echo "python3 $(srcdir)/ctxswitch/python_cor.py \"$$""@\"" >> a.out
    290292        chmod a+x a.out
    291293
    292294ctxswitch-nodejs_coroutine$(EXEEXT):
    293295        $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out
    294         echo "nodejs $(srcdir)/ctxswitch/node_cor.js" >> a.out
     296        echo "nodejs $(srcdir)/ctxswitch/node_cor.js \"$$""@\"" >> a.out
    295297        chmod a+x a.out
    296298
    297299ctxswitch-nodejs_await$(EXEEXT):
    298300        $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out
    299         echo "nodejs $(srcdir)/ctxswitch/node_await.js" >> a.out
     301        echo "nodejs $(srcdir)/ctxswitch/node_await.js \"$$""@\"" >> a.out
    300302        chmod a+x a.out
    301303
     
    309311        $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/ctxswitch/JavaThread.java
    310312        echo "#!/bin/sh" > a.out
    311         echo "java JavaThread" >> a.out
     313        echo "java JavaThread \"$$""@\"" >> a.out
    312314        chmod a+x a.out
    313315
     
    351353        $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/mutex/JavaThread.java
    352354        echo "#!/bin/sh" > a.out
    353         echo "java JavaThread" >> a.out
     355        echo "java JavaThread \"$$""@\"" >> a.out
    354356        chmod a+x a.out
    355357
     
    383385        $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/schedint/JavaThread.java
    384386        echo "#!/bin/sh" > a.out
    385         echo "java JavaThread" >> a.out
     387        echo "java JavaThread \"$$""@\"" >> a.out
    386388        chmod a+x a.out
    387389
     
    450452creation-python_coroutine$(EXEEXT):
    451453        $(BENCH_V_PY)echo "#!/bin/sh" > a.out
    452         echo "python3.7 $(srcdir)/creation/python_cor.py" >> a.out
     454        echo "python3 $(srcdir)/creation/python_cor.py \"$$""@\"" >> a.out
    453455        chmod a+x a.out
    454456
    455457creation-nodejs_coroutine$(EXEEXT):
    456458        $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out
    457         echo "nodejs $(srcdir)/creation/node_cor.js" >> a.out
     459        echo "nodejs $(srcdir)/creation/node_cor.js \"$$""@\"" >> a.out
    458460        chmod a+x a.out
    459461
     
    467469        $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/creation/JavaThread.java
    468470        echo "#!/bin/sh" > a.out
    469         echo "java JavaThread" >> a.out
     471        echo "java JavaThread \"$$""@\"" >> a.out
    470472        chmod a+x a.out
    471473
     
    475477## =========================================================================================================
    476478
    477 compile$(EXEEXT) :              \
     479bcompile$(EXEEXT) :             \
    478480        compile-array.make      \
    479481        compile-attributes.make \
     
    488490
    489491compile-array$(EXEEXT):
    490         $(CFACOMPILE) -fsyntax-only -w $(testdir)/array.cfa
     492        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/array.cfa
    491493
    492494compile-attributes$(EXEEXT):
    493         $(CFACOMPILE) -fsyntax-only -w $(testdir)/attributes.cfa
     495        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/attributes.cfa
    494496
    495497compile-empty$(EXEEXT):
    496         $(CFACOMPILE) -fsyntax-only -w $(srcdir)/compile/empty.cfa
     498        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(srcdir)/compile/empty.cfa
    497499
    498500compile-expression$(EXEEXT):
    499         $(CFACOMPILE) -fsyntax-only -w $(testdir)/expression.cfa
     501        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/expression.cfa
    500502
    501503compile-io$(EXEEXT):
    502         $(CFACOMPILE) -fsyntax-only -w $(testdir)/io1.cfa
     504        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/io1.cfa
    503505
    504506compile-monitor$(EXEEXT):
    505         $(CFACOMPILE) -fsyntax-only -w $(testdir)/concurrent/monitor.cfa
     507        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/concurrent/monitor.cfa
    506508
    507509compile-operators$(EXEEXT):
    508         $(CFACOMPILE) -fsyntax-only -w $(testdir)/operators.cfa
     510        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/operators.cfa
    509511
    510512compile-thread$(EXEEXT):
    511         $(CFACOMPILE) -fsyntax-only -w $(testdir)/concurrent/thread.cfa
     513        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/concurrent/thread.cfa
    512514
    513515compile-typeof$(EXEEXT):
    514         $(CFACOMPILE) -fsyntax-only -w $(testdir)/typeof.cfa
     516        $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/typeof.cfa
    515517
    516518## =========================================================================================================
     
    520522size-cfa$(EXEEXT):
    521523        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/size/size.cfa
     524
     525## =========================================================================================================
     526
     527%-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs
     528        cd $(builddir) && cargo build --release
     529        cp $(builddir)/target/release/$(basename $@) $@
  • benchmark/creation/JavaThread.java

    r3c64c668 r58fe85a  
    11public class JavaThread {
    22        // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator.
    3         // Bijective   
     3        // Bijective
    44        // Cycle length for non-zero values is 4G-1.
    55        // 0 is absorbing and should be avoided -- fixed point.
    66        // The returned value is typically masked to produce a positive value.
    7         static volatile int Ticket = 0 ; 
     7        static volatile int Ticket = 0 ;
    88
    99        private static int nextRandom (int x) {
    10                 if (x == 0) { 
     10                if (x == 0) {
    1111                        // reseed the PRNG
    12                         // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 
    13                         // Note that we use a non-atomic racy increment -- the race is rare and benign. 
    14                         // If the race is a concern switch to an AtomicInteger. 
    15                         // In addition accesses to the RW volatile global "Ticket"  variable are not 
    16                         // (readily) predictable at compile-time so the JIT will not be able to elide 
    17                         // nextRandom() invocations. 
    18                         x = ++Ticket ; 
    19                         if (x == 0) x = 1 ; 
     12                        // Ticket is accessed infrequently and does not constitute a coherence hot-spot.
     13                        // Note that we use a non-atomic racy increment -- the race is rare and benign.
     14                        // If the race is a concern switch to an AtomicInteger.
     15                        // In addition accesses to the RW volatile global "Ticket"  variable are not
     16                        // (readily) predictable at compile-time so the JIT will not be able to elide
     17                        // nextRandom() invocations.
     18                        x = ++Ticket ;
     19                        if (x == 0) x = 1 ;
    2020                }
    2121                x ^= x << 6;
    2222                x ^= x >>> 21;
    2323                x ^= x << 7;
    24                 return x ;   
     24                return x ;
    2525        }
    2626        static int x = 2;
    2727
    28         static private int times = Integer.parseInt("10000") ;
     28        static private long times = Long.parseLong("10000") ;
    2929
    3030        public static class MyThread extends Thread {
     
    3333        }
    3434        public static void helper() throws InterruptedException {
    35                 for(int i = 1; i <= times; i += 1) {
     35                for(long i = 1; i <= times; i += 1) {
    3636                        MyThread m = new MyThread();
    3737                        x = nextRandom( x );
     
    4747        }
    4848        public static void main(String[] args) throws InterruptedException {
    49                 if ( args.length > 2 ) System.exit( 1 );
    50                 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }
     49                if ( args.length > 1 ) System.exit( 1 );
     50                if ( args.length == 1 ) { times = Long.parseLong(args[0]); }
    5151
    52                 for (int i = Integer.parseInt("5"); --i >= 0 ; ) { 
     52                for (int i = Integer.parseInt("5"); --i >= 0 ; ) {
    5353                        InnerMain();
    5454                        Thread.sleep(2000);             // 2 seconds
  • benchmark/creation/cfa_gen.cfa

    r3c64c668 r58fe85a  
    1 #include "bench.h"
     1#include "../bench.h"
    22
    3 struct C {
     3generator G {
    44        volatile int restart; // ensure compiler does not optimize away all the code
    55};
    6 void ?{}( C & c ) { c.restart = 0; }
    7 void main( C & ) {}
     6void ?{}( G & g ) { g.restart = 0; }
     7void main( G & ) {}
    88
    99int main( int argc, char * argv[] ) {
     
    1111        BENCH(
    1212                for ( times ) {
    13                          C c;
     13                         G g;
    1414                },
    1515                result
  • benchmark/ctxswitch/JavaThread.java

    r3c64c668 r58fe85a  
    11public class JavaThread {
    22        // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator.
    3         // Bijective   
     3        // Bijective
    44        // Cycle length for non-zero values is 4G-1.
    55        // 0 is absorbing and should be avoided -- fixed point.
    66        // The returned value is typically masked to produce a positive value.
    7         static volatile int Ticket = 0 ; 
     7        static volatile int Ticket = 0 ;
    88
    99        private static int nextRandom (int x) {
    10                 if (x == 0) { 
     10                if (x == 0) {
    1111                        // reseed the PRNG
    12                         // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 
    13                         // Note that we use a non-atomic racy increment -- the race is rare and benign. 
    14                         // If the race is a concern switch to an AtomicInteger. 
    15                         // In addition accesses to the RW volatile global "Ticket"  variable are not 
    16                         // (readily) predictable at compile-time so the JIT will not be able to elide 
    17                         // nextRandom() invocations. 
    18                         x = ++Ticket ; 
    19                         if (x == 0) x = 1 ; 
     12                        // Ticket is accessed infrequently and does not constitute a coherence hot-spot.
     13                        // Note that we use a non-atomic racy increment -- the race is rare and benign.
     14                        // If the race is a concern switch to an AtomicInteger.
     15                        // In addition accesses to the RW volatile global "Ticket"  variable are not
     16                        // (readily) predictable at compile-time so the JIT will not be able to elide
     17                        // nextRandom() invocations.
     18                        x = ++Ticket ;
     19                        if (x == 0) x = 1 ;
    2020                }
    2121                x ^= x << 6;
    2222                x ^= x >>> 21;
    2323                x ^= x << 7;
    24                 return x ;   
     24                return x ;
    2525        }
    2626        static int x = 2;
    2727
    28         static private int times = Integer.parseInt("100000");
     28        static private long times = Long.parseLong("100000");
    2929
    3030        public static void helper() {
    31                 for(int i = 1; i <= times; i += 1) {
     31                for(long i = 1; i <= times; i += 1) {
    3232                        Thread.yield();
    3333                }
     
    4040        }
    4141        public static void main(String[] args) throws InterruptedException {
    42                 if ( args.length > 2 ) System.exit( 1 );
    43                 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }
     42                if ( args.length > 1 ) System.exit( 1 );
     43                if ( args.length == 1 ) { times = Long.parseLong(args[0]); }
    4444
    4545                for (int i = Integer.parseInt("5"); --i >= 0 ; ) {
  • benchmark/ctxswitch/cfa_cor.cfa

    r3c64c668 r58fe85a  
    22#include <thread.hfa>
    33
    4 #include "bench.h"
     4#include "../bench.h"
    55
    6 coroutine C {} c;
     6coroutine C {};
    77void main( __attribute__((unused)) C & ) {
    8         while () {
    9                 suspend();
     8        for () {
     9                suspend;
    1010        }
    1111}
    1212int main( int argc, char * argv[] ) {
     13        C c;
    1314        BENCH_START()
    1415        BENCH(
  • benchmark/ctxswitch/cfa_gen.cfa

    r3c64c668 r58fe85a  
    11#include "../bench.h"
    22
    3 typedef struct {
    4         void * next;
    5 } C;
    6 
    7 void comain( C * c ) {
    8         if ( __builtin_expect(c->next != 0, 1) ) goto *(c->next);
    9         c->next = &&s1;
     3generator G {};
     4void main( G & ) {
    105        for () {
    11                 return;
    12           s1: ;
     6                suspend;
    137        }
    148}
    159
    1610int main( int argc, char * argv[] ) {
     11        G g;
    1712        BENCH_START()
    18         C c = { 0 };
    1913        BENCH(
    2014                for ( times ) {
    21                         comain( &c );
     15                        resume( g );
    2216                },
    2317                result
  • benchmark/exclude

    r3c64c668 r58fe85a  
    1010interrupt_linux.c
    1111exclude
     12io
    1213Monitor.c
  • benchmark/mutex/JavaThread.java

    r3c64c668 r58fe85a  
    11public class JavaThread {
    22        // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator.
    3         // Bijective   
     3        // Bijective
    44        // Cycle length for non-zero values is 4G-1.
    55        // 0 is absorbing and should be avoided -- fixed point.
    66        // The returned value is typically masked to produce a positive value.
    7         static volatile int Ticket = 0 ; 
     7        static volatile int Ticket = 0 ;
    88
    99        private static int nextRandom (int x) {
    10                 if (x == 0) { 
     10                if (x == 0) {
    1111                        // reseed the PRNG
    12                         // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 
    13                         // Note that we use a non-atomic racy increment -- the race is rare and benign. 
    14                         // If the race is a concern switch to an AtomicInteger. 
    15                         // In addition accesses to the RW volatile global "Ticket"  variable are not 
    16                         // (readily) predictable at compile-time so the JIT will not be able to elide 
    17                         // nextRandom() invocations. 
    18                         x = ++Ticket ; 
    19                         if (x == 0) x = 1 ; 
     12                        // Ticket is accessed infrequently and does not constitute a coherence hot-spot.
     13                        // Note that we use a non-atomic racy increment -- the race is rare and benign.
     14                        // If the race is a concern switch to an AtomicInteger.
     15                        // In addition accesses to the RW volatile global "Ticket"  variable are not
     16                        // (readily) predictable at compile-time so the JIT will not be able to elide
     17                        // nextRandom() invocations.
     18                        x = ++Ticket ;
     19                        if (x == 0) x = 1 ;
    2020                }
    2121                x ^= x << 6;
    2222                x ^= x >>> 21;
    2323                x ^= x << 7;
    24                 return x ;   
     24                return x ;
    2525        }
    2626        static int x = 2;
    2727
    28         static private int times = Integer.parseInt("100000000");
     28        static private long times = Long.parseLong("100000000");
    2929
    3030        public synchronized void noop() {
     
    3434                JavaThread j = new JavaThread();
    3535                // Inhibit biased locking ...
    36                 x = (j.hashCode() ^ System.identityHashCode(j)) | 1 ;     
    37                 for(int i = 1; i <= times; i += 1) {
     36                x = (j.hashCode() ^ System.identityHashCode(j)) | 1 ;
     37                for(long i = 1; i <= times; i += 1) {
    3838                        x = nextRandom(x);
    3939                        j.noop();
     
    4747        }
    4848        public static void main(String[] args) throws InterruptedException {
    49                 if ( args.length > 2 ) System.exit( 1 );
    50                 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }
     49                if ( args.length > 1 ) System.exit( 1 );
     50                if ( args.length == 1 ) { times = Long.parseLong(args[0]); }
    5151
    52                 for (int n = Integer.parseInt("5"); --n >= 0 ; ) { 
     52                for (int n = Integer.parseInt("5"); --n >= 0 ; ) {
    5353                        InnerMain();
    5454                        Thread.sleep(2000);     // 2 seconds
  • benchmark/mutexC/JavaThread.java

    r3c64c668 r58fe85a  
    11class Noop {
    22        // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator.
    3         // Bijective   
     3        // Bijective
    44        // Cycle length for non-zero values is 4G-1.
    55        // 0 is absorbing and should be avoided -- fixed point.
    66        // The returned value is typically masked to produce a positive value.
    7         static volatile int Ticket = 0 ; 
     7        static volatile int Ticket = 0 ;
    88
    99        public static int nextRandom( int x ) {
    10                 if (x == 0) { 
     10                if (x == 0) {
    1111                        // reseed the PRNG
    12                         // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 
    13                         // Note that we use a non-atomic racy increment -- the race is rare and benign. 
    14                         // If the race is a concern switch to an AtomicInteger. 
    15                         // In addition accesses to the RW volatile global "Ticket"  variable are not 
    16                         // (readily) predictable at compile-time so the JIT will not be able to elide 
    17                         // nextRandom() invocations. 
    18                         x = ++Ticket ; 
    19                         if (x == 0) x = 1 ; 
     12                        // Ticket is accessed infrequently and does not constitute a coherence hot-spot.
     13                        // Note that we use a non-atomic racy increment -- the race is rare and benign.
     14                        // If the race is a concern switch to an AtomicInteger.
     15                        // In addition accesses to the RW volatile global "Ticket"  variable are not
     16                        // (readily) predictable at compile-time so the JIT will not be able to elide
     17                        // nextRandom() invocations.
     18                        x = ++Ticket ;
     19                        if (x == 0) x = 1 ;
    2020                }
    2121                x ^= x << 6;
    2222                x ^= x >>> 21;
    2323                x ^= x << 7;
    24                 return x ;   
     24                return x ;
    2525        }
    2626}
     
    4747        static int x = 2;
    4848
    49         static private int times = Integer.parseInt("10000000");
     49        static private long times = Long.parseLong("10000000");
    5050
    5151        public static void call( Monitor m ) throws InterruptedException {
     
    5353                m.go = true;
    5454                //while ( ! m.go2 );
    55                 for ( int i = 0; i < times; i += 1 ) {
     55                for ( long i = 0; i < times; i += 1 ) {
    5656                        m.call();
    5757                        x = Noop.nextRandom( x );
     
    7171        public static void main( String[] args ) throws InterruptedException {
    7272                if ( args.length > 2 ) System.exit( 1 );
    73                 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }
     73                if ( args.length == 2 ) { times = Long.parseLong(args[1]); }
    7474
    75                 if ( args.length > 2 ) System.exit( 1 );
    76                 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }
    77 
    78                 for ( int i = Integer.parseInt("5"); --i >= 0 ; ) {
     75                for ( int i = Integer.parseInt("5"); --i >= 0 ; ) {
    7976                        InnerMain();
    8077                        // Thread.sleep(2000);  // 2 seconds
  • benchmark/schedint/JavaThread.java

    r3c64c668 r58fe85a  
    2424public class JavaThread {
    2525        // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator.
    26         // Bijective   
     26        // Bijective
    2727        // Cycle length for non-zero values is 4G-1.
    2828        // 0 is absorbing and should be avoided -- fixed point.
    2929        // The returned value is typically masked to produce a positive value.
    30         static volatile int Ticket = 0 ; 
     30        static volatile int Ticket = 0 ;
    3131
    3232        private static int nextRandom (int x) {
    33                 if (x == 0) { 
     33                if (x == 0) {
    3434                        // reseed the PRNG
    35                         // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 
    36                         // Note that we use a non-atomic racy increment -- the race is rare and benign. 
    37                         // If the race is a concern switch to an AtomicInteger. 
    38                         // In addition accesses to the RW volatile global "Ticket"  variable are not 
    39                         // (readily) predictable at compile-time so the JIT will not be able to elide 
    40                         // nextRandom() invocations. 
    41                         x = ++Ticket ; 
    42                         if (x == 0) x = 1 ; 
     35                        // Ticket is accessed infrequently and does not constitute a coherence hot-spot.
     36                        // Note that we use a non-atomic racy increment -- the race is rare and benign.
     37                        // If the race is a concern switch to an AtomicInteger.
     38                        // In addition accesses to the RW volatile global "Ticket"  variable are not
     39                        // (readily) predictable at compile-time so the JIT will not be able to elide
     40                        // nextRandom() invocations.
     41                        x = ++Ticket ;
     42                        if (x == 0) x = 1 ;
    4343                }
    4444                x ^= x << 6;
    4545                x ^= x >>> 21;
    4646                x ^= x << 7;
    47                 return x ;   
     47                return x ;
    4848        }
    4949        static int x = 2;
    5050
    51         static private int times = Integer.parseInt("1000000");
     51        static private long times = Long.parseLong("1000000");
    5252
    5353        public static void helper( Monitor m ) throws InterruptedException {
    54                 for(int i = 1; i <= times; i += 1) {
     54                for(long i = 1; i <= times; i += 1) {
    5555                        m.wait();               // relase monitor lock
    5656                        m.next = true;
     
    7575        }
    7676        public static void main(String[] args) throws InterruptedException {
    77                 if ( args.length > 2 ) System.exit( 1 );
    78                 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }
     77                if ( args.length > 1 ) System.exit( 1 );
     78                if ( args.length == 1 ) { times = Long.parseLong(args[0]); }
    7979
    80                 for (int n = Integer.parseInt("5"); --n >= 0 ; ) { 
     80                for (int n = Integer.parseInt("5"); --n >= 0 ; ) {
    8181                        InnerMain();
    8282                        Thread.sleep(2000);     // 2 seconds
  • configure.ac

    r3c64c668 r58fe85a  
    33
    44AC_PREREQ([2.68])
    5 AC_INIT([cfa-cc],[1.0.0.0],[cforall@plg.uwaterloo.ca])
     5AC_INIT([cfa-cc],[1.0.0],[cforall@plg.uwaterloo.ca])
    66AC_CONFIG_AUX_DIR([automake])
    77AC_CONFIG_MACRO_DIRS([automake])
    8 #AC_CONFIG_SRCDIR([src/main.cc])
    98AC_CONFIG_HEADERS([config.h:src/config.h.in])
    109AM_SILENT_RULES([yes])
    1110
    12 m4_include([automake/cfa.m4])
     11m4_include([tools/build/cfa.m4])
    1312
    1413# don't use the default CFLAGS as they unconditonnaly add -O2
    1514: ${CFLAGS=""}
     15: ${CXXFLAGS=""}
    1616
    1717AM_INIT_AUTOMAKE([subdir-objects])
     
    2424#Trasforming cc1 will break compilation
    2525M4CFA_PROGRAM_NAME
     26
     27#==============================================================================
     28# New AST toggling support
     29AH_TEMPLATE([CFA_USE_NEW_AST],[Sets whether or not to use the new-ast, this is adefault value and can be overrided by --old-ast and --new-ast])
     30DEFAULT_NEW_AST="True"
     31AC_ARG_ENABLE(new-ast,
     32        [  --enable-new-ast     whether or not to use new ast as the default AST algorithm],
     33        [case "${enableval}" in
     34                yes) newast=true ; DEFAULT_NEW_AST="True"  ;;
     35                no)  newast=false; DEFAULT_NEW_AST="False" ;;
     36                *) AC_MSG_ERROR([bad value ${enableval} for --enable-new-ast]) ;;
     37        esac],[newast=true])
     38AC_DEFINE_UNQUOTED([CFA_USE_NEW_AST], $newast)
     39AC_SUBST(DEFAULT_NEW_AST)
    2640
    2741#==============================================================================
     
    6478        enable_distcc=$enableval, enable_distcc=no)
    6579
     80AC_ARG_WITH(bwlimit,
     81        [  --with-bwlimit=RATE     RATE the maximum rate at which rsync will be limited when using distributed builds],
     82        [], [])
     83
    6684AM_CONDITIONAL([ENABLE_DISTCC], [test x$enable_distcc = xyes])
    6785HAS_DISTCC="False"
     
    85103# Create variables for commonly used targets
    86104
    87 TOP_SRCDIR="$(readlink -m $ac_confdir/)/"
    88 TOP_BUILDDIR="$(readlink -m $ac_pwd/)/"
     105TOP_SRCDIR="$(readlink -e $ac_abs_confdir/)/"
     106TOP_BUILDDIR="$(readlink -e $ac_pwd/)/"
    89107
    90108AC_DEFINE_UNQUOTED(TOP_SRCDIR, "$TOP_SRCDIR", [Top src directory])
     
    121139                \'--enable-gprofiler=*) ;;
    122140                \'--disable-gprofiler) ;;
     141
     142                # skip the target hosts
     143                \'--enable-new-ast=*) ;;
     144                \'--disable-new-ast) ;;
     145
     146                # skip this, it only causes problems
     147                \'--srcdir=*) ;;
    123148
    124149                # append all other arguments to the sub configure arguments
     
    186211
    187212        LIBCFA_TARGET_DIRS="${LIBCFA_TARGET_DIRS} ${lib_dir}"
     213        LIBCFA_1TARGET_DIR="${lib_dir}"
    188214        LIBCFA_TARGET_MAKEFILES="${LIBCFA_TARGET_MAKEFILES} ${lib_dir}/Makefile"
    189215
     
    197223
    198224AC_SUBST(LIBCFA_TARGET_DIRS)
     225AC_SUBST(LIBCFA_1TARGET_DIR)
    199226AC_SUBST(LIBCFA_TARGET_MAKEFILES)
    200227
     
    262289        driver/Makefile
    263290        src/Makefile
    264         benchmark/Makefile
     291        libcfa/Makefile:libcfa/Makefile.dist.in
    265292        tests/Makefile
    266         longrun_tests/Makefile
    267         tools/Makefile
    268         tools/prettyprinter/Makefile
    269293        ])
     294
     295# Some of our makefile don't need to be distributed
     296AM_CONDITIONAL([CFORALL_DISTRIBUTE], [test -e $TOP_SRCDIR/autogen.sh])
     297AM_COND_IF([CFORALL_DISTRIBUTE], [
     298        AC_CONFIG_FILES([
     299                longrun_tests/Makefile
     300                benchmark/Makefile
     301                benchmark/io/http/Makefile
     302                tools/Makefile
     303                tools/prettyprinter/Makefile
     304        ])
     305
     306        AC_OUTPUT(benchmark/Cargo.toml)
     307])
    270308
    271309AC_CONFIG_LINKS([tests/test.py:tests/test.py])
  • doc/LaTeXmacros/common.tex

    r3c64c668 r58fe85a  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Fri May 24 07:59:54 2019
    14 %% Update Count     : 382
     13%% Last Modified On : Mon Oct  5 09:34:46 2020
     14%% Update Count     : 464
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    3636% Names used in the document.
    3737
     38\usepackage{xspace}
    3839\newcommand{\CFAIcon}{\textsf{C}\raisebox{\depth}{\rotatebox{180}{\textsf{A}}}\xspace} % Cforall symbolic name
    3940\newcommand{\CFA}{\protect\CFAIcon}             % safe for section/caption
     
    5455\newlength{\parindentlnth}
    5556\setlength{\parindentlnth}{\parindent}
    56 
    57 \newcommand{\LstBasicStyle}[1]{{\lst@basicstyle{#1}}}
    58 \newcommand{\LstKeywordStyle}[1]{{\lst@basicstyle{\lst@keywordstyle{#1}}}}
    59 \newcommand{\LstCommentStyle}[1]{{\lst@basicstyle{\lst@commentstyle{#1}}}}
    60 
    61 \newlength{\gcolumnposn}                                % temporary hack because lstlisting does not handle tabs correctly
    62 \newlength{\columnposn}
    63 \setlength{\gcolumnposn}{2.75in}
    64 \setlength{\columnposn}{\gcolumnposn}
    65 \newcommand{\C}[2][\@empty]{\ifx#1\@empty\else\global\setlength{\columnposn}{#1}\global\columnposn=\columnposn\fi\hfill\makebox[\textwidth-\columnposn][l]{\lst@basicstyle{\LstCommentStyle{#2}}}}
    66 \newcommand{\CRT}{\global\columnposn=\gcolumnposn}
    67 
    68 % allow escape sequence in lstinline
    69 %\usepackage{etoolbox}
    70 %\patchcmd{\lsthk@TextStyle}{\let\lst@DefEsc\@empty}{}{}{\errmessage{failed to patch}}
    7157
    7258\usepackage{pslatex}                                    % reduce size of san serif font
     
    241227}%
    242228
     229\usepackage{listings}                                                                   % format program code
    243230\usepackage{lstlang}
    244 
    245 \newcommand{\CFADefaults}{%
     231\makeatletter
     232
     233\newcommand{\LstBasicStyle}[1]{{\lst@basicstyle{#1}}}
     234\newcommand{\LstKeywordStyle}[1]{{\lst@basicstyle{\lst@keywordstyle{#1}}}}
     235\newcommand{\LstCommentStyle}[1]{{\lst@basicstyle{\lst@commentstyle{#1}}}}
     236
     237\newlength{\gcolumnposn}                                % temporary hack because lstlisting does not handle tabs correctly
     238\newlength{\columnposn}
     239\setlength{\gcolumnposn}{2.75in}
     240\setlength{\columnposn}{\gcolumnposn}
     241\newcommand{\C}[2][\@empty]{\ifx#1\@empty\else\global\setlength{\columnposn}{#1}\global\columnposn=\columnposn\fi\hfill\makebox[\textwidth-\columnposn][l]{\lst@basicstyle{\LstCommentStyle{#2}}}}
     242\newcommand{\CRT}{\global\columnposn=\gcolumnposn}
     243
     244% allow escape sequence in lstinline
     245%\usepackage{etoolbox}
     246%\patchcmd{\lsthk@TextStyle}{\let\lst@DefEsc\@empty}{}{}{\errmessage{failed to patch}}
     247
     248% allow adding to lst literate
     249\def\addToLiterate#1{\protect\edef\lst@literate{\unexpanded\expandafter{\lst@literate}\unexpanded{#1}}}
     250\lst@Key{add to literate}{}{\addToLiterate{#1}}
     251\makeatother
     252
     253\newcommand{\CFAStyle}{%
    246254\lstset{
    247 language=CFA,
    248255columns=fullflexible,
    249256basicstyle=\linespread{0.9}\sf,                 % reduce line spacing and use sanserif font
     
    260267belowskip=3pt,
    261268% replace/adjust listing characters that look bad in sanserif
    262 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1
     269literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1
    263270        {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 {`}{\ttfamily\upshape\hspace*{-0.1ex}`}1
    264271        {<-}{$\leftarrow$}2 {=>}{$\Rightarrow$}2 {->}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.075ex}}}\kern-0.2ex\textgreater}2,
     272}% lstset
     273}% CFAStyle
     274
     275\ifdefined\CFALatin% extra Latin-1 escape characters
     276\lstnewenvironment{cfa}[1][]{
     277\lstset{
     278language=CFA,
    265279moredelim=**[is][\color{red}]{Ā®}{Ā®},    % red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-.
    266280moredelim=**[is][\color{blue}]{ß}{ß},   % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_
    267281moredelim=**[is][\color{OliveGreen}]{¢}{¢}, % green highlighting ¢...¢ (cent symbol) emacs: C-q M-"
    268282moredelim=[is][\lstset{keywords={}}]{¶}{¶}, % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^
     283% replace/adjust listing characters that look bad in sanserif
     284add to literate={`}{\ttfamily\upshape\hspace*{-0.1ex}`}1
    269285}% lstset
    270 }% CFADefaults
    271 \newcommand{\CFAStyle}{%
    272 \CFADefaults
     286\lstset{#1}
     287}{}
    273288% inline code Ā©...Ā© (copyright symbol) emacs: C-q M-)
    274289\lstMakeShortInlineĀ©                                    % single-character for \lstinline
    275 }% CFAStyle
    276 
    277 \lstnewenvironment{cfa}[1][]
    278 {\CFADefaults\lstset{#1}}
    279 {}
     290\else% regular ASCI characters
     291\lstnewenvironment{cfa}[1][]{
     292\lstset{
     293language=CFA,
     294escapechar=\$,                                                  % LaTeX escape in CFA code
     295moredelim=**[is][\color{red}]{@}{@},    % red highlighting @...@
     296}% lstset
     297\lstset{#1}
     298}{}
     299% inline code @...@ (at symbol)
     300\lstMakeShortInline@                                    % single-character for \lstinline
     301\fi%
    280302
    281303% Local Variables: %
  • doc/LaTeXmacros/lstlang.sty

    r3c64c668 r58fe85a  
    88%% Created On       : Sat May 13 16:34:42 2017
    99%% Last Modified By : Peter A. Buhr
    10 %% Last Modified On : Tue Jan  8 14:40:33 2019
    11 %% Update Count     : 21
     10%% Last Modified On : Wed Sep 23 22:40:04 2020
     11%% Update Count     : 24
    1212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1313
     
    115115                auto, _Bool, catch, catchResume, choose, _Complex, __complex, __complex__, __const, __const__,
    116116                coroutine, disable, dtype, enable, exception, __extension__, fallthrough, fallthru, finally,
    117                 __float80, float80, __float128, float128, forall, ftype, _Generic, _Imaginary, __imag, __imag__,
     117                __float80, float80, __float128, float128, forall, ftype, generator, _Generic, _Imaginary, __imag, __imag__,
    118118                inline, __inline, __inline__, __int128, int128, __label__, monitor, mutex, _Noreturn, one_t, or,
    119                 otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, thread,
     119                otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, suspend, thread,
    120120                _Thread_local, throw, throwResume, timeout, trait, try, ttype, typeof, __typeof, __typeof__,
    121121                virtual, __volatile, __volatile__, waitfor, when, with, zero_t,
     
    125125
    126126% C++ programming language
    127 \lstdefinelanguage{C++}[ANSI]{C++}{}
     127\lstdefinelanguage{C++}[ANSI]{C++}{
     128        morekeywords={nullptr,}
     129}
    128130
    129131% uC++ programming language, based on ANSI C++
  • doc/bibliography/pl.bib

    r3c64c668 r58fe85a  
    99%    Predefined journal names:
    1010%  acmcs: Computing Surveys             acta: Acta Infomatica
    11 @string{acta="Acta Infomatica"}
    1211%  cacm: Communications of the ACM
    1312%  ibmjrd: IBM J. Research & Development ibmsj: IBM Systems Journal
     
    2221%  tcs: Theoretical Computer Science
    2322
     23@string{acta="Acta Infomatica"}
    2424string{ieeepds="IEEE Transactions on Parallel and Distributed Systems"}
    2525@string{ieeepds="IEEE Trans. Parallel Distrib. Syst."}
     
    124124    series      = {ACM Distinguished Dissertations},
    125125    year        = 1983,
     126}
     127
     128@article{Zhang19,
     129    keywords    = {Algebraic effects, dynamic scoping, exceptions, parametricity, type systems},
     130    author      = {Zhang, Yizhou and Myers, Andrew C.},
     131    title       = {Abstraction-safe Effect Handlers via Tunneling},
     132    journal     = {Proc. ACM Program. Lang.},
     133    issue_date  = {January 2019},
     134    volume      = {3},
     135    number      = {POPL},
     136    month       = jan,
     137    year        = {2019},
     138    issn        = {2475-1421},
     139    pages       = {5:1--5:29},
     140    articleno   = {5},
     141    publisher   = {ACM},
     142    address     = {New York, NY, USA},
     143}
     144
     145@inproceedings{Zhang16,
     146    keywords    = {Exception tunneling, Genus, exception handling},
     147    author      = {Zhang, Yizhou and Salvaneschi, Guido and Beightol, Quinn and Liskov, Barbara and Myers, Andrew C.},
     148    title       = {Accepting Blame for Safe Tunneled Exceptions},
     149    booktitle   = {Proceedings of the 37th ACM SIGPLAN Conference on Programming Language Design and Implementation},
     150    series      = {PLDI'16},
     151    year        = {2016},
     152    location    = {Santa Barbara, CA, USA},
     153    pages       = {281--295},
     154    publisher   = {ACM},
     155    address     = {New York, NY, USA},
    126156}
    127157
     
    398428    journal     = sigplan,
    399429    year        = 1981,
    400     month       = feb, volume = 16, number = 2, pages = {48-52},
     430    month       = feb,
     431    volume      = 16,
     432    number      = 2,
     433    pages       = {48-52},
    401434    comment     = {
    402435        A one-pass, top-down algorithm for overload resolution.  Input is a
     
    477510    title       = {An Alternative to Subclassing},
    478511    journal     = sigplan,
    479     volume      = {21},    number = {11},
     512    volume      = {21},
     513    number      = {11},
    480514    pages       = {424-428},
    481     month       = nov, year = 1986,
     515    month       = nov,
     516    year        = 1986,
    482517    comment     = {
    483518        The Smalltalk class hierarchy has three uses: factoring out code;
     
    533568    isbn        = {3-540-66538-2},
    534569    location    = {Toulouse, France},
    535     doi         = {http://doi.acm.org/10.1145/318773.319251},
    536570    publisher   = {Springer},
    537571    address     = {London, UK},
     
    631665    year        = 2010,
    632666    pages       = {39--50},
    633     numpages    = {12},
    634667    publisher   = {IEEE Computer Society},
    635668    address     = {Washington, DC, USA},
     
    922955}
    923956
     957@manual{C99,
     958    keywords    = {ISO/IEC C 9899},
     959    contributer = {pabuhr@plg},
     960    key         = {C99},
     961    title       = {C Programming Language {ISO/IEC} 9899:1999(E)},
     962    edition     = {2nd},
     963    organization= {International Standard Organization},
     964    address     = {Geneva, Switzerland},
     965    year        = 1999,
     966    note        = {\href{https://webstore.ansi.org/Standards/INCITS/INCITSISOIEC98991999R2005}{https://webstore.ansi.org/\-Standards/\-INCITS/\-INCITSISOIEC98991999R2005}},
     967}
     968
    924969@manual{C11,
    925970    keywords    = {ISO/IEC C 11},
     
    928973    title       = {C Programming Language {ISO/IEC} 9889:2011-12},
    929974    edition     = {3rd},
    930     publisher   = {International Standard Organization},
    931     address     = {\href{https://www.iso.org/standard/57853.html}{https://\-www.iso.org/\-standard/\-57853.html}},
     975    organization= {International Standard Organization},
     976    address     = {Geneva, Switzerland},
    932977    year        = 2012,
     978    note        = {\href{https://www.iso.org/standard/57853.html}{https://\-www.iso.org/\-standard/\-57853.html}},
    933979}
    934980
     
    938984    key         = {Concepts},
    939985    title       = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming language -- Extensions for concepts {ISO/IEC} {TS} 19217:2015},
    940     publisher   = {International Standard Organization},
    941     address     = {\href{https://www.iso.org/standard/64031.html}{https://\-www.iso.org/\-standard/\-64031.html}},
     986    organization= {International Standard Organization},
     987    address     = {Geneva, Switzerland},
    942988    year        = 2015,
     989    note        = {\href{https://www.iso.org/standard/64031.html}{https://\-www.iso.org/\-standard/\-64031.html}},
    943990}
    944991
     
    9571004}
    9581005
    959 @misc{CforallBenchMarks,
     1006@misc{CforallConcurrentBenchmarks,
    9601007    contributer = {pabuhr@plg},
    9611008    key         = {Cforall Benchmarks},
    9621009    author      = {{\textsf{C}{$\mathbf{\forall}$} Benchmarks}},
    963     howpublished= {\href{https://plg.uwaterloo.ca/~cforall/benchmark.tar}{https://\-plg.uwaterloo.ca/\-$\sim$cforall/\-benchmark.tar}},
     1010    howpublished= {\href{https://github.com/cforall/ConcurrentBenchmarks_SPE20}{https://\-github.com/\-cforall/\-ConcurrentBenchmarks\_SPE20}},
    9641011}
    9651012
     
    11051152    title       = {C\# Language Specification, Standard ECMA-334},
    11061153    organization= {ECMA International Standardizing Information and Communication Systems},
     1154    address     = {Geneva, Switzerland},
    11071155    month       = jun,
    11081156    year        = 2006,
     
    12541302    title       = {Programming Languages -- {Cobol} ISO/IEC 1989:2014},
    12551303    edition     = {2nd},
    1256     institution = {International Standard Organization},
    1257     address     = {\href{https://www.iso.org/standard/51416.html}{https://\-www.iso.org/\-standard/\-51416.html}},
     1304    organization= {International Standard Organization},
     1305    address     = {Geneva, Switzerland},
    12581306    year        = 2014,
     1307    note        = {\href{https://www.iso.org/standard/51416.html}{https://\-www.iso.org/\-standard/\-51416.html}},
    12591308}
    12601309
     
    13051354    location    = {London, United Kingdom},
    13061355    pages       = {41--53},
    1307     numpages    = {13},
    1308     url         = {http://doi.acm.org/10.1145/360204.360207},
    1309     doi         = {10.1145/360204.360207},
    1310     acmid       = {360207},
    13111356    publisher   = {ACM},
    13121357    address     = {New York, NY, USA},
     
    16141659    title       = {$\mu${C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Annotated Reference Manual, Version 7.0.0},
    16151660    organization= {University of Waterloo},
     1661    address     = {Waterloo Ontario, Canada},
    16161662    month       = sep,
    16171663    year        = 2018,
     
    19331979    title       = {Cooperating Sequential Processes},
    19341980    institution = {Technological University},
    1935     address     = {Eindhoven, Netherlands},
     1981    address     = {Eindhoven, Neth.},
    19361982    year        = 1965,
    19371983    note        = {Reprinted in \cite{Genuys68} pp. 43--112.}
     
    19421988    author      = {Adya, Atul and Howell, Jon and Theimer, Marvin and Bolosky, William J. and Douceur, John R.},
    19431989    title       = {Cooperative Task Management Without Manual Stack Management},
    1944     booktitle   = {Proceedings of the General Track of the Annual Conference on USENIX Annual Technical Conference},
     1990    booktitle   = {Proc. of the General Track USENIX Tech. Conf.},
    19451991    series      = {ATEC '02},
    19461992    year        = {2002},
     
    20462092    author      = {Walter Bright and Andrei Alexandrescu},
    20472093    organization= {Digital Mars},
     2094    address     = {Vienna Virginia, U.S.A.},
    20482095    year        = 2016,
    20492096    note        = {\href{http://dlang.org/spec/spec.html}{http://\-dlang.org/\-spec/\-spec.html}},
     
    24082455    year        = 1993,
    24092456    pages       = {201--208},
    2410     url         = {http://doi.acm.org/10.1145/155360.155580},
    24112457    publisher   = {ACM},
    24122458    address     = {New York, NY, USA},
     
    26062652    location    = {Boulder, Colorado, USA},
    26072653    pages       = {91--97},
    2608     numpages    = {7},
    26092654    publisher   = {ACM},
    26102655    address     = {New York, NY, USA},
     
    26372682    issn        = {0004-5411},
    26382683    pages       = {215--225},
    2639     numpages    = {11},
    2640     url         = {http://doi.acm.org/10.1145/321879.321884},
    2641     doi         = {10.1145/321879.321884},
    2642     acmid       = {321884},
    26432684    publisher   = {ACM},
    26442685    address     = {New York, NY, USA},
     
    27082749}
    27092750
     2751@misc{Drepper13,
     2752    keywords    = {thread-local storage},
     2753    contributer = {pabuhr@plg},
     2754    author      = {Ulrich Drepper},
     2755    title       = {{ELF} Handling For Thread-Local Storage},
     2756    year        = 2013,
     2757    month       = aug,
     2758    note        = {WikipediA},
     2759    howpublished= {\href{http://www.akkadia.org/drepper/tls.pdf}
     2760                  {http://\-www.akkadia.org/\-drepper/\-tls.pdf}},
     2761}
     2762
    27102763@misc{Turley99,
    27112764    keywords    = {embedded system, micrprocessor},
     
    27182771    howpublished= {\href{https://www.eetimes.com/author.asp?sectionid=36&doc_id=1287712}
    27192772                  {https://\-www.eetimes.com/\-author.asp?sectionid=\-36&doc_id=1287712}},
     2773}
     2774
     2775@article{Xiao19,
     2776    keywords    = {bug classification, fault trigger, Linux operating system, regression bug},
     2777    contributer = {pabuhr@plg},
     2778    author      = {Guanping Xiao and Zheng Zheng and Beibei Yin and Kishor S. Trivedi and Xiaoting Du and Kai-Yuan Cai},
     2779    title       = {An Empirical Study of Fault Triggers in the Linux Operating System: An Evolutionary Perspective},
     2780    journal     = {IEEE Transactions on Reliability},
     2781    month       = dec,
     2782    year        = 2019,
     2783    volume      = 68,
     2784    number      = 4,
     2785    pages       = {1356-1383},
    27202786}
    27212787
     
    31373203}
    31383204
     3205@inproceedings{Palix11,
     3206    keywords    = {Linux, fault-finding tools},
     3207    contributer = {pabuhr@plg},
     3208    author      = {Nicolas Palix and Ga\"el Thomas and Suman Saha and Christophe Calv\`es and Julia Lawall and Gilles Muller},
     3209    title       = {Faults in Linux: Ten Years Later},
     3210    booktitle   = {Proc. of the 16 International Conf. on Arch. Support for Prog. Lang. and Oper. Sys.},
     3211    series      = {ASPLOS'11},
     3212    month       = mar,
     3213    year        = 2011,
     3214    location    = {Newport Beach, California, USA},
     3215    pages       = {305-318},
     3216    publisher   = {ACM},
     3217    address     = {New York, NY, USA},
     3218}
     3219
    31393220@article{Lamport87,
    31403221    keywords    = {software solutions, mutual exclusion, fast},
     
    32583339    issn        = {0001-0782},
    32593340    pages       = {107--115},
    3260     numpages    = {9},
    3261     url         = {http://doi.acm.org/10.1145/1538788.1538814},
    3262     doi         = {10.1145/1538788.1538814},
    3263     acmid       = {1538814},
    32643341    publisher   = {ACM},
    32653342    address     = {New York, NY, USA},
     
    32833360    title       = {Programming Languages -- {Fortran} Part 1:Base Language ISO/IEC 1539-1:2010},
    32843361    edition     = {3rd},
    3285     publisher   = {International Standard Organization},
    3286     address     = {\href{https://www.iso.org/standard/50459.html}{https://\-www.iso.org/\-standard/\-50459.html}},
     3362    organization= {International Standard Organization},
     3363    address     = {Geneva, Switzerland},
    32873364    year        = 2010,
     3365    note        = {\href{https://www.iso.org/standard/50459.html}{https://\-www.iso.org/\-standard/\-50459.html}},
    32883366}
    32893367
     
    32943372    title       = {Programming Languages -- {Fortran} Part 1:Base Language ISO/IEC 1539-1:2018},
    32953373    edition     = {4rd},
    3296     publisher   = {International Standard Organization},
    3297     address     = {\href{https://www.iso.org/standard/72320.html}{https://\-www.iso.org/\-standard/\-72320.html}},
     3374    organization= {International Standard Organization},
     3375    address     = {Geneva, Switzerland},
    32983376    year        = 2018,
     3377    note        = {\href{https://www.iso.org/standard/72320.html}{https://\-www.iso.org/\-standard/\-72320.html}},
    32993378}
    33003379
     
    36643743}
    36653744
     3745@mastersthesis{Radhakrishnan19,
     3746    author      = {Srihari Radhakrishnan},
     3747    title       = {High Performance Web Servers: A Study In Concurrent Programming Models},
     3748    school      = {School of Computer Sc., University of Waterloo},
     3749    year        = 2019,
     3750    optaddress  = {Waterloo, Ontario, Canada, N2L 3G1},
     3751    note        = {\href{https://uwspace.uwaterloo.ca/handle/10012/14706}{https://\-uwspace.uwaterloo.ca/\-handle/\-10012/\-14706}},
     3752}
     3753
    36663754@article{katzenelson83b,
    36673755    contributer = {gjditchfield@plg},
     
    36973785    pages       = {115-138},
    36983786    year        = 1971,
     3787}
     3788
     3789@inproceedings{Hagersten03,
     3790    keywords    = {cache storage, parallel architectures, performance evaluation, shared memory systems},
     3791    author      = {Zoran Radovi\'{c} and Erik Hagersten},
     3792    title       = {Hierarchical backoff locks for nonuniform communication architectures},
     3793    booktitle   = {Proceedings of the Ninth International Symposium on High-Performance Computer Architecture},
     3794    year        = {2003},
     3795    location    = {Anaheim, CA, USA},
     3796    pages       = {241-252},
     3797    publisher   = {IEEE},
    36993798}
    37003799
     
    43654464}
    43664465
     4466@misc{gccValueLabels,
     4467    keywords    = {gcc extension, value labels},
     4468    contributer = {pabuhr@plg},
     4469    key         = {Labels as Values},
     4470    author      = {{gcc Extension}},
     4471    title       = {Labels as Values},
     4472    year        = {since gcc-3},
     4473    howpublished= {\href{https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html}
     4474                  {https:\-//gcc.gnu.org/\-onlinedocs/\-gcc/\-Labels-as-Values.html}},
     4475}
     4476
    43674477@mastersthesis{Clarke90,
    43684478    keywords    = {concurrency, postponing requests},
     
    44234533}
    44244534
     4535@misc{libfibre,
     4536    key         = {libfibre},
     4537    author      = {Martin Karsten},
     4538    title       = {{libfibre:~User-Level Threading Runtime}},
     4539    howpublished= {\href{https://git.uwaterloo.ca/mkarsten/libfibre}
     4540                  {https://\-git.uwaterloo.ca/\-mkarsten/\-libfibre}},
     4541    note        = {[Online; accessed 2020-04-15]},
     4542}
     4543
    44254544@article{Linda,
    44264545    keywords    = {Linda, concurrency},
     
    44564575}
    44574576
     4577@inproceedings{Fang06,
     4578    author      = {Fang, Yi and McMillan, Kenneth L. and Pnueli, Amir and Zuck, Lenore D.},
     4579    editor      = {Najm, Elie and Pradat-Peyre, Jean-Fran{\c{c}}ois and Donzeau-Gouge, V{\'e}ronique Vigui{\'e}},
     4580    title       = {Liveness by Invisible Invariants},
     4581    booktitle   = {Formal Techniques for Networked and Distributed Systems - FORTE 2006},
     4582    year        = 2006,
     4583    publisher   = {Springer Berlin Heidelberg},
     4584    address     = {Berlin, Heidelberg},
     4585    pages       = {356--371},
     4586}
     4587
    44584588@article{Pierce00,
    4459     keywords    = {Scala},
     4589    keywords    = {Scala, polymorphism, subtyping, type inference},
    44604590    contributer = {a3moss@uwaterloo.ca},
    44614591    author      = {Pierce, Benjamin C. and Turner, David N.},
     
    44694599    issn        = {0164-0925},
    44704600    pages       = {1--44},
    4471     numpages    = {44},
    4472     url         = {http://doi.acm.org/10.1145/345099.345100},
    4473     doi         = {10.1145/345099.345100},
    4474     acmid       = {345100},
    44754601    publisher   = {ACM},
    44764602    address     = {New York, NY, USA},
    4477     keywords    = {polymorphism, subtyping, type inference},
    44784603}
     4604
     4605@article{Dice15,
     4606    keywords    = {Concurrency, NUMA, hierarchical locks, locks, multicore, mutex, mutual exclusion, spin locks},
     4607    author      = {Dice, David and Marathe, Virendra J. and Shavit, Nir},
     4608    title       = {Lock Cohorting: A General Technique for Designing NUMA Locks},
     4609    journal     = {ACM Trans. Parallel Comput.},
     4610    issue_date  = {January 2015},
     4611    volume      = 1,
     4612    number      = 2,
     4613    month       = feb,
     4614    year        = 2015,
     4615    pages       = {13:1--13:42},
     4616    publisher   = {ACM},
     4617    address     = {New York, NY, USA},
     4618}
    44794619
    44804620@article{Sundell08,
     
    45544694    journal     = sigplan,
    45554695    year        = 1989,
    4556     month       = jun, volume = 24, number = 6, pages = {37-48},
     4696    month       = jun,
     4697    volume      = 24,
     4698    number      = 6,
     4699    pages       = {37-48},
    45574700    abstract    = {
    45584701        This paper describes a scheme we have used to manage a large
     
    46104753    address     = {New York, NY, USA},
    46114754}
     4755
    46124756@techreport{Mesa,
    46134757    keywords    = {monitors, packages},
     
    46164760    title       = {Mesa Language Manual},
    46174761    institution = {Xerox Palo Alto Research Center},
     4762    address     = {Palo Alto, California, U.S.A.},
    46184763    number      = {CSL--79--3},
    46194764    month       = apr,
     
    46254770    contributer = {pabuhr@plg},
    46264771    author      = {Gregory R. Andrews},
    4627     title       = {A Method for Solving Synronization Problems},
     4772    title       = {A Method for Solving Synchronization Problems},
    46284773    journal     = scp,
    46294774    volume      = 13,
     
    49505095    title       = {Multiple Inheritance for {C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}}},
    49515096    booktitle   = {Proceedings of the Spring '87 EUUG Conference},
    4952     month       = may, year = 1987
     5097    month       = may,
     5098    year        = 1987,
    49535099}
    49545100
     
    49955141    year        = 1986,
    49965142    pages       = {313--326},
    4997     numpages    = {14},
    49985143    publisher   = {ACM},
    49995144    address     = {New York, NY, USA},
     
    50115156    year        = 1986,
    50125157    pages       = {327--348},
    5013     numpages    = {22},
    50145158    publisher   = {ACM},
    50155159    address     = {New York, NY, USA},
     
    52085352    year        = 2005,
    52095353    pages       = {146-196},
    5210     numpages    = {51},
    52115354    publisher   = {ACM},
    52125355    address     = {New York, NY, USA},
     
    53545497    year        = 2000,
    53555498    pages       = {29-46},
    5356     note        = {OOPSLA'00, Oct. 15--19, 2000, Minneapolis, Minnesota, U.S.A.},
     5499    note        = {OOPSLA'00, Oct. 15--19, 2000, Minneapolis, Minn., U.S.A.},
    53575500}
    53585501
     
    54685611    location    = {San Diego, California, USA},
    54695612    pages       = {101--112},
    5470     numpages    = {12},
    5471     url         = {http://doi.acm.org/10.1145/2535838.2535878},
    5472     doi         = {10.1145/2535838.2535878},
    5473     acmid       = {2535878},
    54745613    publisher   = {ACM},
    54755614    address     = {New York, NY, USA},
     
    55755714    issn        = {0362-1340},
    55765715    pages       = {30--42},
    5577     numpages    = {13},
    5578     url         = {http://doi.acm.org/10.1145/947586.947589},
    5579     doi         = {10.1145/947586.947589},
    55805716    publisher   = {ACM},
    55815717    address     = {New York, NY, USA}
     
    61146250}
    61156251
     6252@article{Bauer15,
     6253    keywords    = {resumption exceptions, theory},
     6254    contributer = {pabuhr@plg},
     6255    author      = {Andrej Bauer and Matija Pretnar},
     6256    title       = {Programming with Algebraic Effects and Handlers},
     6257    journal     = {Journal of Logical and Algebraic Methods in Programming},
     6258    publisher   = {Elsevier BV},
     6259    volume      = 84,
     6260    number      = 1,
     6261    month       = jan,
     6262    year        = 2015,
     6263    pages       = {108-123},
     6264}
     6265
    61166266@book{Butenhof97,
    61176267    keywords    = {PThreads, concurrency},
     
    61626312    title       = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:1998},
    61636313    edition     = {1st},
    6164     publisher   = {International Standard Organization},
    6165     address     = {\href{https://www.iso.org/standard/25845.html}{https://\-www.iso.org/\-standard/\-25845.html}},
     6314    organization  = {International Standard Organization},
     6315    address     = {Geneva, Switzerland},
    61666316    year        = 1998,
     6317    note        = {\href{https://www.iso.org/standard/25845.html}{https://\-www.iso.org/\-standard/\-25845.html}},
    61676318}
    61686319
     
    61736324    title       = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:2014},
    61746325    edition     = {4th},
    6175     publisher   = {International Standard Organization},
    6176     address     = {\href{https://www.iso.org/standard/64029.html}{https://\-www.iso.org/\-standard/\-64029.html}},
     6326    organization= {International Standard Organization},
     6327    address     = {Geneva, Switzerland},
    61776328    year        = 2014,
     6329    note        = {\href{https://www.iso.org/standard/64029.html}{https://\-www.iso.org/\-standard/\-64029.html}},
    61786330}
    61796331
     
    61846336    title       = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:2017},
    61856337    edition     = {5th},
    6186     publisher   = {International Standard Organization},
    6187     address     = {\href{https://www.iso.org/standard/68564.html}{https://\-www.iso.org/\-standard/\-68564.html}},
     6338    organization= {International Standard Organization},
     6339    address     = {Geneva, Switzerland},
    61886340    year        = 2017,
     6341    note        = {\href{https://www.iso.org/standard/68564.html}{https://\-www.iso.org/\-standard/\-68564.html}},
    61896342}
    61906343
     
    63186471    title       = {The Programming Language Concurrent Pascal},
    63196472    journal     = ieeese,
    6320     volume      = 2,
     6473    volume      = {SE-1},
     6474    number      = 2,
    63216475    month       = jun,
    63226476    year        = 1975,
    6323     pages       = {199-206}
     6477    pages       = {199-207}
    63246478}
    63256479
     
    64996653    issn        = {0164-0925},
    65006654    pages       = {429-475},
    6501     url         = {http://doi.acm.org/10.1145/1133651.1133653},
    6502     doi         = {10.1145/1133651.1133653},
    6503     acmid       = {1133653},
    65046655    publisher   = {ACM},
    65056656    address     = {New York, NY, USA},
     
    65316682}
    65326683
     6684@article{Aravind09,
     6685    author      = {Alex A. Aravind and Wim H. Hesselink},
     6686    title       = {A Queue Based Mutual Exclusion Algorithm},
     6687    journal     = acta,
     6688    volume      = 46,
     6689    pages       = {73--86},
     6690    year        = 2009,
     6691}
     6692
    65336693% R
    65346694
     
    65746734    title       = {Programming languages -- {Ada} ISO/IEC 8652:2012},
    65756735    edition     = {3rd},
    6576     publisher   = {International Standard Organization},
    6577     address     = {\href{https://www.iso.org/standard/61507.html}{https://\-www.iso.org/\-standard/\-61507.html}},
     6736    organization= {International Standard Organization},
     6737    address     = {Geneva, Switzerland},
    65786738    year        = 2012,
     6739    note        = {\href{https://www.iso.org/standard/61507.html}{https://\-www.iso.org/\-standard/\-61507.html}},
    65796740}
    65806741
     
    68797040    issn        = {0001-0782},
    68807041    pages       = {565--569},
    6881     numpages    = {5},
    6882     url         = {http://doi.acm.org/10.1145/359545.359566},
    6883     doi         = {10.1145/359545.359566},
    6884     acmid       = {359566},
    68857042    publisher   = {ACM},
    68867043    address     = {New York, NY, USA}
     
    69007057    issn        = {0362-1340},
    69017058    pages       = {145--147},
    6902     numpages    = {3},
    6903     url         = {http://doi.acm.org/10.1145/122598.122614},
    6904     doi         = {10.1145/122598.122614},
    6905     acmid       = {122614},
    69067059    publisher   = {ACM},
    69077060    address     = {New York, NY, USA},
     
    70067159    issn        = {0362-1340},
    70077160    pages       = {82--87},
    7008     numpages    = {6},
    7009     url         = {http://doi.acm.org/10.1145/947680.947688},
    7010     doi         = {10.1145/947680.947688},
    70117161    publisher   = {ACM},
    70127162    address     = {New York, NY, USA},
     
    71537303}
    71547304
     7305@article{Cascaval08,
     7306    author      = {Cascaval, Calin and Blundell, Colin and Michael, Maged and Cain, Harold W. and Wu, Peng and Chiras, Stefanie and Chatterjee, Siddhartha},
     7307    title       = {Software Transactional Memory: Why Is It Only a Research Toy?},
     7308    journal     = {Queue},
     7309    volume      = {6},
     7310    number      = {5},
     7311    month       = sep,
     7312    year        = {2008},
     7313    pages       = {40:46--40:58},
     7314    publisher   = {ACM},
     7315    address     = {New York, NY, USA},
     7316}
     7317
    71557318@article{Dijkstra65a,
    71567319    keywords    = {N-thread software-solution mutual exclusion},
     
    73637526    year        = 1974,
    73647527    pages       = {261-301},
    7365     issn        = {0360-0300},
    7366     doi         = {http://doi.acm.org/10.1145/356635.356640},
    73677528    publisher   = {ACM},
    73687529    address     = {New York, NY, USA},
     
    74547615    publisher   = {ACM Press},
    74557616    address     = {New York, NY, USA},
    7456     doi         = {http://doi.acm.org/10.1145/356586.356588},
    74577617}
    74587618
     
    75827742    title       = {The Thoth System: Multi-Process Structuring and Portability},
    75837743    publisher   = {American Elsevier},
     7744    address     = {New York, New York, U.S.A.},
    75847745    year        = 1982
    75857746}
     
    77557916    howpublished= {\href{https://projects.eclipse.org/proposals/trace-compass}{https://\-projects.eclipse.org/\-proposals/\-trace-compass}},
    77567917}
    7757  
     7918
     7919@inproceedings{Boehm09,
     7920    author      = {Boehm, Hans-J.},
     7921    title       = {Transactional Memory Should Be an Implementation Technique, Not a Programming Interface},
     7922    booktitle   = {Proceedings of the First USENIX Conference on Hot Topics in Parallelism},
     7923    series      = {HotPar'09},
     7924    year        = {2009},
     7925    location    = {Berkeley, California},
     7926    publisher   = {USENIX Association},
     7927    address     = {Berkeley, CA, USA},
     7928}
     7929
    77587930@article{Leroy00,
    77597931    keywords    = {type-systems, exceptions},
     
    78057977    number      = {2},
    78067978    pages       = {204-214},
    7807     month       = apr, year = 1988,
     7979    month       = apr,
     7980    year        = 1988,
    78087981    comment     = {
    78097982        Extended record types add fields to their base record.  Assignment
     
    79048077}
    79058078
     8079@article{Karsten20,
     8080    author      = {Karsten, Martin and Barghi, Saman},
     8081    title       = {{User-level Threading: Have Your Cake and Eat It Too}},
     8082    year        = {2020},
     8083    issue_date  = {March 2020},
     8084    publisher   = {Association for Computing Machinery},
     8085    address     = {New York, NY, USA},
     8086    volume      = {4},
     8087    number      = {1},
     8088    url         = {https://doi.org/10.1145/3379483},
     8089    doi         = {10.1145/3379483},
     8090    journal     = {Proc. ACM Meas. Anal. Comput. Syst.},
     8091    month       = mar,
     8092    numpages    = {30},
     8093}
     8094
    79068095@techreport{Harmony,
    79078096    keywords    = {messages, concurrency},
     
    79198108    contributer = {gjditchfield@plg},
    79208109    author      = {Henry Lieverman},
    7921     title       = {Using Prototypical Objects to Implement Shared Behavior in
    7922                   Object Oriented Systems},
     8110    title       = {Using Prototypical Objects to Implement Shared Behavior in Object Oriented Systems},
    79238111    journal     = sigplan,
    7924     month       = nov, year = 1986,
    7925     volume      = 21, number = 11, pages = {214-223}
     8112    month       = nov,
     8113    year        = 1986,
     8114    volume      = 21,
     8115    number      = 11,
     8116    pages       = {214-223}
    79268117}
    79278118
     
    81108301    issn        = {0004-5411},
    81118302    pages       = {245--281},
    8112     numpages    = {37},
    8113     url         = {http://doi.acm.org/10.1145/62.2160},
    8114     doi         = {10.1145/62.2160},
    8115     acmid       = {2160},
    81168303    publisher   = {ACM},
    81178304    address     = {New York, NY, USA},
     
    81268313    contributer = {pabuhr@plg},
    81278314    author      = {Boehm, Hans-J. and Adve, Sarita V.},
    8128     title       = {You Don'T Know Jack About Shared Variables or Memory Models},
     8315    title       = {You Don't Know Jack About Shared Variables or Memory Models},
    81298316    journal     = cacm,
    81308317    volume      = 55,
  • doc/man/cfa.1

    r3c64c668 r58fe85a  
    1111.\" Created On       : Wed Jul 26 22:34:47 2017
    1212.\" Last Modified By : Peter A. Buhr
    13 .\" Last Modified On : Thu Jul 27 10:29:29 2017
    14 .\" Update Count     : 44
     13.\" Last Modified On : Wed Sep  2 17:59:53 2020
     14.\" Update Count     : 78
    1515.\"
    1616.\" nroff -man cfa.1
     
    2323.ds Cf "Cforall
    2424.\"
    25 .TH cfa 1 2017-07-27 cfa-\*(Mg
     25.TH CFA 1 "2020-09-2" cfa-\*(Mg "\*(Cf Project"
    2626.SH NAME
    27 cfa \- \*(Cf Translator and Runtime Library
     27cfa \- \*(Cf project translator and runtime library to enhance C
    2828.SH SYNOPSIS
    29 cfa [gcc-options] [C/\*(Cf source-files] [assembler/loader files]
     29cfa [cfa/gcc-options]
     30    [cfa/c source-files]
     31    [assembler/loader files]
    3032.SH DESCRIPTION
     33\*(Cf (C-for-all) is an open-source project extending ISO C with modern safety and productivity features, while still ensuring backwards compatibility with C and its programmers.
     34
    3135The cfa command compiles C and \*(Cf source files and links C/\*(Cf object
    3236files named on the command line.
     
    3438The cfa command introduces a translator pass over the specified source files
    3539after the C preprocessor but before the C compilation.  The translator converts
    36 new \*(Cf constructs into C statements.  The cfa command also provides the
    37 runtime library, which is linked with each \*(Cf application.
     40new \*(Cf constructs into C statements.  The cfa command also provides a fully
     41concurrent (user-level threads) runtime library, which is linked with the
     42\*(Cf application.
    3843
    3944The command line options depend on the particular C compiler used (gcc/clang
    4045supported).  As with most C compilers, the output is sent to the file a.out(5)
    4146unless the -o option is present on the command line.  See the reference pages
    42 for gcc(1) for more information.
     47for gcc(1) for more information on command line options.
    4348.SH OPTIONS
    4449When multiple conflicting options appear on the command line, e.g.,
     
    5055All of the options available to the gcc compiler are available to the cfa
    5156translator.  The following gcc flags are implicitly turned on:
    52 .IP -std=gnu99 3
    53 The 1999 C standard plus GNU extensions.
    54 .IP -fgnu89-inline
    55 Use the traditional GNU semantics for inline routines in C99 mode, which allows inline routines in header files.
     57.IP "-std=gnu11" 3
     58The 2011 C standard plus GNU extensions.
     59.IP "-fgnu89-inline"
     60Use the traditional GNU semantics for inline routines in C11 mode, which allows inline routines in header files.
     61.IP "-imacros stdbool.h"
     62Include stdbool.h to get defines for bool/true/false.
     63.IP "-latomic -lm"
     64Provide access to double-wide CAS instruction and math library.
    5665.LP
    5766The following additional options are available:
    58 .IP -CFA 3
     67.IP "-CFA" 3
    5968Only the C preprocessor and the \*(Cf translator steps are performed and the transformed program is written to standard output, which makes it possible to examine the code generated by the \*(Cf translator.
    6069The generated code starts with the standard \*(Cf prelude.
    61 .IP -debug
     70.IP "-debug"
    6271The program is linked with the debugging version of the runtime system.
    6372The debug version performs runtime checks to help during the debugging phase of a \*(Cf program, but can substantially slow program execution.
    6473The runtime checks should only be removed after the program is completely debugged.
    6574.B This option is the default.
    66 .IP -nodebug
     75.IP "-nodebug"
    6776The program is linked with the non-debugging version of the runtime system, so the execution of the program is faster.
    6877.I However, no runtime checks or asserts are performed so errors usually result in abnormal program behaviour or termination.
    69 .IP -help
     78.IP "-help"
    7079Information about the set of \*(Cf compilation flags is printed.
    71 .IP -nohelp
     80.IP "-nohelp"
    7281Information about the set of \*(Cf compilation flags is not printed.
    7382.B This option is the default.
    74 .IP -quiet
     83.IP "-quiet"
    7584The \*(Cf compilation message is not printed at the beginning of a compilation.
    76 .IP -noquiet
     85.IP "-noquiet"
    7786The \*(Cf compilation message is printed at the beginning of a compilation.
    7887.B This option is the default.
     
    8190available.  These variables allow conditional compilation of programs that must
    8291work differently in these situations.
    83 .IP __CFA_MAJOR__ 3
     92.IP "__CFA_MAJOR__" 3
    8493is available during preprocessing and its value is the major version number of \*(Cf.
    85 .IP __CFA_MINOR__
     94.IP "__CFA_MINOR__"
    8695is available during preprocessing and its value is the minor version number of \*(Cf.
    87 .IP __CFA_PATCH__
     96.IP "__CFA_PATCH__"
    8897is available during preprocessing and its value is the patch level number of \*(Cf.
    8998.IP "__CFA__, __CFORALL__, and __cforall"
    9099are always available during preprocessing and have no value.
    91 .IP __CFA_DEBUG__
     100.IP "__CFA_DEBUG__"
    92101is available during preprocessing if the -debug compilation option is
    93102specified.
     
    116125.SH REFERENCES
    117126.HP 3
    118 \*(Cf Reference and Rational Manual
     127.I \*(Cf Home Page
    119128.br
    120 http://plg.uwaterloo.ca/~cforall/refrat.pdf
     129https://cforall.uwaterloo.ca
    121130.HP
    122131.I \*(Cf User Manual
    123132.br
    124 http://plg.uwaterloo.ca/~cforall/user.pdf
     133https://cforall.uwaterloo.ca/doc/user.pdf
     134.SH BUILDS
     135Nightly builds are available here https://cforall.uwaterloo.ca/jenkins
    125136.SH BUGS
    126 Bugs should be reported to trac@plg.cs.uwaterloo.ca.
     137Bugs reportss are available here https://cforall.uwaterloo.ca/trac
    127138.SH COPYRIGHT
    128139\*(Cf is covered under the licence agreement in the distribution.
    129140.SH AUTHORS
    130141Andrew Beach, Richard Bilson, Peter A. Buhr, Thierry Delisle, Glen Ditchfield,
    131 Rodolfo G. Esteves, Aaron Moss, Rob Schluntz
     142Rodolfo G. Esteves, Aaron Moss, Rob Schluntz, Mubeen Zulfiqar
  • doc/papers/AMA/AMA-stix/ama/WileyNJD-v2.cls

    r3c64c668 r58fe85a  
    24442444     \@afterheading}
    24452445
    2446 \renewcommand\section{\@startsection{section}{1}{\z@}{-25pt \@plus -2pt \@minus -2pt}{12\p@}{\sectionfont}}%
    2447 \renewcommand\subsection{\@startsection{subsection}{2}{\z@}{-22pt \@plus -2pt \@minus -2pt}{5\p@}{\subsectionfont}}%
    2448 \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}{-20pt \@plus -2pt \@minus -2pt}{2\p@}{\subsubsectionfont}}%
     2446\renewcommand\section{\@startsection{section}{1}{\z@}{-20pt \@plus -2pt \@minus -2pt}{7\p@}{\sectionfont}}%
     2447\renewcommand\subsection{\@startsection{subsection}{2}{\z@}{-18pt \@plus -2pt \@minus -2pt}{5\p@}{\subsectionfont}}%
     2448\renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}{-16pt \@plus -2pt \@minus -2pt}{2\p@}{\subsubsectionfont}}%
    24492449%
    24502450\newskip\secruleskip\secruleskip8.5\p@%
  • doc/papers/concurrency/Paper.tex

    r3c64c668 r58fe85a  
    6161\newcommand{\CCseventeen}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}17\xspace} % C++17 symbolic name
    6262\newcommand{\CCtwenty}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}20\xspace} % C++20 symbolic name
    63 \newcommand{\Csharp}{C\raisebox{-0.7ex}{\Large$^\sharp$}\xspace} % C# symbolic name
     63\newcommand{\Csharp}{C\raisebox{-0.7ex}{\large$^\sharp$}\xspace} % C# symbolic name
    6464
    6565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     
    9999\newcommand{\CRT}{\global\columnposn=\gcolumnposn}
    100100
    101 % Denote newterms in particular font and index them without particular font and in lowercase, e.g., \newterm{abc}.
    102 % The option parameter provides an index term different from the new term, e.g., \newterm[\texttt{abc}]{abc}
     101% Denote newterms in particular font and index them without particular font and in lowercase, \eg \newterm{abc}.
     102% The option parameter provides an index term different from the new term, \eg \newterm[\texttt{abc}]{abc}
    103103% The star version does not lowercase the index information, e.g., \newterm*{IBM}.
    104104\newcommand{\newtermFontInline}{\emph}
     
    110110\newcommand{\abbrevFont}{\textit}                       % set empty for no italics
    111111\@ifundefined{eg}{
    112 \newcommand{\EG}{\abbrevFont{e}\abbrevFont{g}}
     112%\newcommand{\EG}{\abbrevFont{e}\abbrevFont{g}}
     113\newcommand{\EG}{for example}
    113114\newcommand*{\eg}{%
    114115        \@ifnextchar{,}{\EG}%
     
    117118}}{}%
    118119\@ifundefined{ie}{
    119 \newcommand{\IE}{\abbrevFont{i}\abbrevFont{e}}
     120%\newcommand{\IE}{\abbrevFont{i}\abbrevFont{e}}
     121\newcommand{\IE}{that is}
    120122\newcommand*{\ie}{%
    121123        \@ifnextchar{,}{\IE}%
     
    127129\newcommand*{\etc}{%
    128130        \@ifnextchar{.}{\ETC}%
    129         {\ETC.\xspace}%
     131                {\ETC.\xspace}%
    130132}}{}%
    131133\@ifundefined{etal}{
    132134\newcommand{\ETAL}{\abbrevFont{et}~\abbrevFont{al}}
    133135\newcommand*{\etal}{%
    134         \@ifnextchar{.}{\protect\ETAL}%
    135                 {\protect\ETAL.\xspace}%
     136        \@ifnextchar{.}{\ETAL}%
     137                {\ETAL.\xspace}%
    136138}}{}%
    137139\@ifundefined{viz}{
     
    163165                __float80, float80, __float128, float128, forall, ftype, generator, _Generic, _Imaginary, __imag, __imag__,
    164166                inline, __inline, __inline__, __int128, int128, __label__, monitor, mutex, _Noreturn, one_t, or,
    165                 otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, thread,
     167                otype, restrict, resume, __restrict, __restrict__, __signed, __signed__, _Static_assert, suspend, thread,
    166168                _Thread_local, throw, throwResume, timeout, trait, try, ttype, typeof, __typeof, __typeof__,
    167169                virtual, __volatile, __volatile__, waitfor, when, with, zero_t},
    168170        moredirectives={defined,include_next},
    169171        % replace/adjust listing characters that look bad in sanserif
    170         literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1
     172        literate={-}{\makebox[1ex][c]{\raisebox{0.5ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1
    171173                {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 % {`}{\ttfamily\upshape\hspace*{-0.1ex}`}1
    172174                {<}{\textrm{\textless}}1 {>}{\textrm{\textgreater}}1
     
    197199                _Else, _Enable, _Event, _Finally, _Monitor, _Mutex, _Nomutex, _PeriodicTask, _RealTimeTask,
    198200                _Resume, _Select, _SporadicTask, _Task, _Timeout, _When, _With, _Throw},
    199 }
    200 \lstdefinelanguage{Golang}{
    201         morekeywords=[1]{package,import,func,type,struct,return,defer,panic,recover,select,var,const,iota,},
    202         morekeywords=[2]{string,uint,uint8,uint16,uint32,uint64,int,int8,int16,int32,int64,
    203                 bool,float32,float64,complex64,complex128,byte,rune,uintptr, error,interface},
    204         morekeywords=[3]{map,slice,make,new,nil,len,cap,copy,close,true,false,delete,append,real,imag,complex,chan,},
    205         morekeywords=[4]{for,break,continue,range,goto,switch,case,fallthrough,if,else,default,},
    206         morekeywords=[5]{Println,Printf,Error,},
    207         sensitive=true,
    208         morecomment=[l]{//},
    209         morecomment=[s]{/*}{*/},
    210         morestring=[b]',
    211         morestring=[b]",
    212         morestring=[s]{`}{`},
    213201}
    214202
     
    238226{}
    239227\lstnewenvironment{C++}[1][]                            % use C++ style
    240 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}}
     228{\lstset{language=C++,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}}
    241229{}
    242230\lstnewenvironment{uC++}[1][]
    243 {\lstset{#1}}
     231{\lstset{language=uC++,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}}
    244232{}
    245233\lstnewenvironment{Go}[1][]
    246 {\lstset{language=Golang,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}}
     234{\lstset{language=Golang,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}}
    247235{}
    248236\lstnewenvironment{python}[1][]
    249 {\lstset{language=python,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}}
     237{\lstset{language=python,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}}
     238{}
     239\lstnewenvironment{java}[1][]
     240{\lstset{language=java,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}}
    250241{}
    251242
     
    262253}
    263254
    264 \newbox\myboxA
    265 \newbox\myboxB
    266 \newbox\myboxC
    267 \newbox\myboxD
     255\newsavebox{\myboxA}
     256\newsavebox{\myboxB}
     257\newsavebox{\myboxC}
     258\newsavebox{\myboxD}
    268259
    269260\title{\texorpdfstring{Advanced Control-flow and Concurrency in \protect\CFA}{Advanced Control-flow in Cforall}}
     
    275266\address[1]{\orgdiv{Cheriton School of Computer Science}, \orgname{University of Waterloo}, \orgaddress{\state{Waterloo, ON}, \country{Canada}}}
    276267
    277 \corres{*Peter A. Buhr, Cheriton School of Computer Science, University of Waterloo, 200 University Avenue West, Waterloo, ON, N2L 3G1, Canada. \email{pabuhr{\char`\@}uwaterloo.ca}}
     268\corres{*Peter A. Buhr, Cheriton School of Computer Science, University of Waterloo, 200 University Avenue West, Waterloo, ON N2L 3G1, Canada. \email{pabuhr{\char`\@}uwaterloo.ca}}
    278269
    279270% \fundingInfo{Natural Sciences and Engineering Research Council of Canada}
    280271
    281272\abstract[Summary]{
    282 \CFA is a polymorphic, non-object-oriented, concurrent, backwards-compatible extension of the C programming language.
     273\CFA is a polymorphic, nonobject-oriented, concurrent, backwards compatible extension of the C programming language.
    283274This paper discusses the design philosophy and implementation of its advanced control-flow and concurrent/parallel features, along with the supporting runtime written in \CFA.
    284 These features are created from scratch as ISO C has only low-level and/or unimplemented concurrency, so C programmers continue to rely on library features like pthreads.
     275These features are created from scratch as ISO C has only low-level and/or unimplemented concurrency, so C programmers continue to rely on library approaches like pthreads.
    285276\CFA introduces modern language-level control-flow mechanisms, like generators, coroutines, user-level threading, and monitors for mutual exclusion and synchronization.
    286277% Library extension for executors, futures, and actors are built on these basic mechanisms.
    287278The runtime provides significant programmer simplification and safety by eliminating spurious wakeup and monitor barging.
    288 The runtime also ensures multiple monitors can be safely acquired \emph{simultaneously} (deadlock free), and this feature is fully integrated with all monitor synchronization mechanisms.
     279The runtime also ensures multiple monitors can be safely acquired in a deadlock-free way, and this feature is fully integrated with all monitor synchronization mechanisms.
    289280All control-flow features integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers.
    290281Experimental results show comparable performance of the new features with similar mechanisms in other concurrent programming languages.
    291282}%
    292283
    293 \keywords{generator, coroutine, concurrency, parallelism, thread, monitor, runtime, C, \CFA (Cforall)}
     284\keywords{C \CFA (Cforall) coroutine concurrency generator monitor parallelism runtime thread}
    294285
    295286
    296287\begin{document}
    297 \linenumbers                                            % comment out to turn off line numbering
     288%\linenumbers                           % comment out to turn off line numbering
    298289
    299290\maketitle
     
    302293\section{Introduction}
    303294
    304 This paper discusses the design philosophy and implementation of advanced language-level control-flow and concurrent/parallel features in \CFA~\cite{Moss18,Cforall} and its runtime, which is written entirely in \CFA.
    305 \CFA is a modern, polymorphic, non-object-oriented\footnote{
    306 \CFA has features often associated with object-oriented programming languages, such as constructors, destructors, virtuals and simple inheritance.
    307 However, functions \emph{cannot} be nested in structures, so there is no lexical binding between a structure and set of functions (member/method) implemented by an implicit \lstinline@this@ (receiver) parameter.},
    308 backwards-compatible extension of the C programming language.
    309 In many ways, \CFA is to C as Scala~\cite{Scala} is to Java, providing a \emph{research vehicle} for new typing and control-flow capabilities on top of a highly popular programming language allowing immediate dissemination.
    310 Within the \CFA framework, new control-flow features are created from scratch because ISO \Celeven defines only a subset of the \CFA extensions, where the overlapping features are concurrency~\cite[\S~7.26]{C11}.
    311 However, \Celeven concurrency is largely wrappers for a subset of the pthreads library~\cite{Butenhof97,Pthreads}, and \Celeven and pthreads concurrency is simple, based on thread fork/join in a function and mutex/condition locks, which is low-level and error-prone;
    312 no high-level language concurrency features are defined.
    313 Interestingly, almost a decade after publication of the \Celeven standard, neither gcc-8, clang-9 nor msvc-19 (most recent versions) support the \Celeven include @threads.h@, indicating little interest in the C11 concurrency approach (possibly because the effort to add concurrency to \CC).
    314 Finally, while the \Celeven standard does not state a threading model, the historical association with pthreads suggests implementations would adopt kernel-level threading (1:1)~\cite{ThreadModel}.
    315 
     295\CFA~\cite{Moss18,Cforall} is a modern, polymorphic, nonobject-oriented\footnote{
     296\CFA has object-oriented features, such as constructors, destructors, and simple trait/interface inheritance.
     297% Go interfaces, Rust traits, Swift Protocols, Haskell Type Classes and Java Interfaces.
     298% "Trait inheritance" works for me. "Interface inheritance" might also be a good choice, and distinguish clearly from implementation inheritance.
     299% You'll want to be a little bit careful with terms like "structural" and "nominal" inheritance as well. CFA has structural inheritance (I think Go as well) -- it's inferred based on the structure of the code.
     300% Java, Rust, and Haskell (not sure about Swift) have nominal inheritance, where there needs to be a specific statement that "this type inherits from this type".
     301However, functions \emph{cannot} be nested in structures and there is no mechanism to designate a function parameter as a receiver, \lstinline@this@, parameter.},
     302, backward-compatible extension of the C programming language.
     303In many ways, \CFA is to C as Scala~\cite{Scala} is to Java, providing a vehicle for new typing and control-flow capabilities on top of a highly popular programming language\footnote{
     304The TIOBE index~\cite{TIOBE} for May 2020 ranks the top five \emph{popular} programming languages as C 17\%, Java 16\%, Python 9\%, \CC 6\%, and \Csharp 4\% = 52\%, and over the past 30 years, C has always ranked either first or second in popularity.}
     305allowing immediate dissemination.
     306This paper discusses the design philosophy and implementation of \CFA's advanced control-flow and concurrent/parallel features, along with the supporting runtime written in \CFA.
     307
     308% The call/return extensions retain state between callee and caller versus losing the callee's state on return;
     309% the concurrency extensions allow high-level management of threads.
     310
     311The \CFA control-flow framework extends ISO \Celeven~\cite{C11} with new call/return and concurrent/parallel control-flow.
     312Call/return control-flow with argument and parameter passing appeared in the first programming languages.
     313Over the past 50 years, call/return has been augmented with features like static and dynamic call, exceptions (multilevel return) and generators/coroutines (see Section~\ref{s:StatefulFunction}).
     314While \CFA has mechanisms for dynamic call (algebraic effects~\cite{Zhang19}) and exceptions\footnote{
     315\CFA exception handling will be presented in a separate paper.
     316The key feature that dovetails with this paper is nonlocal exceptions allowing exceptions to be raised across stacks, with synchronous exceptions raised among coroutines and asynchronous exceptions raised among threads, similar to that in \uC~\cite[\S~5]{uC++}}
     317, this work only discusses retaining state between calls via generators and coroutines.
     318\newterm{Coroutining} was introduced by Conway~\cite{Conway63}, discussed by Knuth~\cite[\S~1.4.2]{Knuth73V1}, implemented in Simula67~\cite{Simula67}, formalized by Marlin~\cite{Marlin80}, and is now popular and appears in old and new programming languages: CLU~\cite{CLU}, \Csharp~\cite{Csharp}, Ruby~\cite{Ruby}, Python~\cite{Python}, JavaScript~\cite{JavaScript}, Lua~\cite{Lua}, \CCtwenty~\cite{C++20Coroutine19}.
     319Coroutining is sequential execution requiring direct handoff among coroutines, \ie only the programmer is controlling execution order.
     320If coroutines transfer to an internal event-engine for scheduling the next coroutines (as in async-await), the program transitions into the realm of concurrency~\cite[\S~3]{Buhr05a}.
     321Coroutines are only a stepping stone toward concurrency where the commonality is that coroutines and threads retain state between calls.
     322
     323\Celeven and \CCeleven define concurrency~\cite[\S~7.26]{C11}, but it is largely wrappers for a subset of the pthreads library~\cite{Pthreads}.\footnote{Pthreads concurrency is based on simple thread fork and join in a function and mutex or condition locks, which is low-level and error-prone}
     324Interestingly, almost a decade after the \Celeven standard, the most recent versions of gcc, clang, and msvc do not support the \Celeven include @threads.h@, indicating no interest in the C11 concurrency approach (possibly because of the recent effort to add concurrency to \CC).
     325While the \Celeven standard does not state a threading model, the historical association with pthreads suggests implementations would adopt kernel-level threading (1:1)~\cite{ThreadModel}, as for \CC.
    316326In contrast, there has been a renewed interest during the past decade in user-level (M:N, green) threading in old and new programming languages.
    317 As multi-core hardware became available in the 1980/90s, both user and kernel threading were examined.
     327As multicore hardware became available in the 1980/1990s, both user and kernel threading were examined.
    318328Kernel threading was chosen, largely because of its simplicity and fit with the simpler operating systems and hardware architectures at the time, which gave it a performance advantage~\cite{Drepper03}.
    319329Libraries like pthreads were developed for C, and the Solaris operating-system switched from user (JDK 1.1~\cite{JDK1.1}) to kernel threads.
    320 As a result, languages like Java, Scala, Objective-C~\cite{obj-c-book}, \CCeleven~\cite{C11}, and C\#~\cite{Csharp} adopt the 1:1 kernel-threading model, with a variety of presentation mechanisms.
    321 From 2000 onwards, languages like Go~\cite{Go}, Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, D~\cite{D}, and \uC~\cite{uC++,uC++book} have championed the M:N user-threading model, and many user-threading libraries have appeared~\cite{Qthreads,MPC,Marcel}, including putting green threads back into Java~\cite{Quasar}.
    322 The main argument for user-level threading is that it is lighter weight than kernel threading (locking and context switching do not cross the kernel boundary), so there is less restriction on programming styles that encourage large numbers of threads performing medium work units to facilitate load balancing by the runtime~\cite{Verch12}.
     330As a result, many languages adopt the 1:1 kernel-threading model, like Java (Scala), Objective-C~\cite{obj-c-book}, \CCeleven~\cite{C11}, C\#~\cite{Csharp} and Rust~\cite{Rust}, with a variety of presentation mechanisms.
     331From 2000 onward, several language implementations have championed the M:N user-threading model, like Go~\cite{Go}, Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, D~\cite{D}, and \uC~\cite{uC++,uC++book}, including putting green threads back into Java~\cite{Quasar}, and many user-threading libraries have appeared~\cite{Qthreads,MPC,Marcel}.
     332The main argument for user-level threading is that it is lighter weight than kernel threading because locking and context switching do not cross the kernel boundary, so there is less restriction on programming styles that encourages large numbers of threads performing medium-sized work to facilitate load balancing by the runtime~\cite{Verch12}.
    323333As well, user-threading facilitates a simpler concurrency approach using thread objects that leverage sequential patterns versus events with call-backs~\cite{Adya02,vonBehren03}.
    324 Finally, performant user-threading implementations (both time and space) meet or exceed direct kernel-threading implementations, while achieving the programming advantages of high concurrency levels and safety.
    325 
    326 A further effort over the past two decades is the development of language memory models to deal with the conflict between language features and compiler/hardware optimizations, \ie some language features are unsafe in the presence of aggressive sequential optimizations~\cite{Buhr95a,Boehm05}.
    327 The consequence is that a language must provide sufficient tools to program around safety issues, as inline and library code is all sequential to the compiler.
    328 One solution is low-level qualifiers and functions (\eg @volatile@ and atomics) allowing \emph{programmers} to explicitly write safe (race-free~\cite{Boehm12}) programs.
    329 A safer solution is high-level language constructs so the \emph{compiler} knows the optimization boundaries, and hence, provides implicit safety.
    330 This problem is best known with respect to concurrency, but applies to other complex control-flow, like exceptions\footnote{
    331 \CFA exception handling will be presented in a separate paper.
    332 The key feature that dovetails with this paper is nonlocal exceptions allowing exceptions to be raised across stacks, with synchronous exceptions raised among coroutines and asynchronous exceptions raised among threads, similar to that in \uC~\cite[\S~5]{uC++}
    333 } and coroutines.
    334 Finally, language solutions allow matching constructs with language paradigm, \ie imperative and functional languages often have different presentations of the same concept to fit their programming model.
     334Finally, performant user-threading implementations, both in time and space, meet or exceed direct kernel-threading implementations, while achieving the programming advantages of high concurrency levels and safety.
     335
     336A further effort over the past two decades is the development of language memory models to deal with the conflict between language features and compiler/hardware optimizations, \eg some language features are unsafe in the presence of aggressive sequential optimizations~\cite{Buhr95a,Boehm05}.
     337The consequence is that a language must provide sufficient tools to program around safety issues, as inline and library code is compiled as sequential without any explicit concurrent directive.
     338One solution is low-level qualifiers and functions, \eg @volatile@ and atomics, allowing \emph{programmers} to explicitly write safe, race-free~\cite{Boehm12} programs.
     339A safer solution is high-level language constructs so the \emph{compiler} knows the concurrency boundaries, \ie where mutual exclusion and synchronization are acquired and released, and provide implicit safety at and across these boundaries.
     340While the optimization problem is best known with respect to concurrency, it applies to other complex control-flows like exceptions and coroutines.
     341As well, language solutions allow matching the language paradigm with the approach, \eg matching the functional paradigm with data-flow programming or the imperative paradigm with thread programming.
    335342
    336343Finally, it is important for a language to provide safety over performance \emph{as the default}, allowing careful reduction of safety for performance when necessary.
    337 Two concurrency violations of this philosophy are \emph{spurious wakeup} (random wakeup~\cite[\S~8]{Buhr05a}) and \emph{barging}\footnote{
    338 The notion of competitive succession instead of direct handoff, \ie a lock owner releases the lock and an arriving thread acquires it ahead of preexisting waiter threads.
    339 } (signals-as-hints~\cite[\S~8]{Buhr05a}), where one is a consequence of the other, \ie once there is spurious wakeup, signals-as-hints follow.
    340 However, spurious wakeup is \emph{not} a foundational concurrency property~\cite[\S~8]{Buhr05a}, it is a performance design choice.
    341 Similarly, signals-as-hints are often a performance decision.
    342 We argue removing spurious wakeup and signals-as-hints make concurrent programming significantly safer because it removes local non-determinism and matches with programmer expectation.
    343 (Author experience teaching concurrency is that students are highly confused by these semantics.)
    344 Clawing back performance, when local non-determinism is unimportant, should be an option not the default.
    345 
    346 \begin{comment}
    347 Most augmented traditional (Fortran 18~\cite{Fortran18}, Cobol 14~\cite{Cobol14}, Ada 12~\cite{Ada12}, Java 11~\cite{Java11}) and new languages (Go~\cite{Go}, Rust~\cite{Rust}, and D~\cite{D}), except \CC, diverge from C with different syntax and semantics, only interoperate indirectly with C, and are not systems languages, for those with managed memory.
    348 As a result, there is a significant learning curve to move to these languages, and C legacy-code must be rewritten.
    349 While \CC, like \CFA, takes an evolutionary approach to extend C, \CC's constantly growing complex and interdependent features-set (\eg objects, inheritance, templates, etc.) mean idiomatic \CC code is difficult to use from C, and C programmers must expend significant effort learning \CC.
    350 Hence, rewriting and retraining costs for these languages, even \CC, are prohibitive for companies with a large C software-base.
    351 \CFA with its orthogonal feature-set, its high-performance runtime, and direct access to all existing C libraries circumvents these problems.
    352 \end{comment}
    353 
    354 \CFA embraces user-level threading, language extensions for advanced control-flow, and safety as the default.
    355 We present comparative examples so the reader can judge if the \CFA control-flow extensions are better and safer than those in other concurrent, imperative programming languages, and perform experiments to show the \CFA runtime is competitive with other similar mechanisms.
     344Two concurrency violations of this philosophy are \emph{spurious} or \emph{random wakeup}~\cite[\S~9]{Buhr05a}, and \emph{barging}\footnote{
     345Barging is competitive succession instead of direct handoff, \ie after a lock is released both arriving and preexisting waiter threads compete to acquire the lock.
     346Hence, an arriving thread can temporally \emph{barge} ahead of threads already waiting for an event, which can repeat indefinitely leading to starvation of waiter threads.
     347} or signals-as-hints~\cite[\S~8]{Buhr05a}, where one is a consequence of the other, \ie once there is spurious wakeup, barging follows.
     348(Author experience teaching concurrency is that students are confused by these semantics.)
     349However, spurious wakeup is \emph{not} a foundational concurrency property~\cite[\S~9]{Buhr05a};
     350it is a performance design choice.
     351We argue removing spurious wakeup and signals-as-hints make concurrent programming simpler and safer as there is less local nondeterminism to manage.
     352If barging acquisition is allowed, its specialized performance advantage should be available as an option not the default.
     353
     354\CFA embraces language extensions for advanced control-flow, user-level threading, and safety as the default.
     355We present comparative examples to support our argument that the \CFA control-flow extensions are as expressive and safe as those in other concurrent imperative programming languages, and perform experiments to show the \CFA runtime is competitive with other similar mechanisms.
    356356The main contributions of this work are:
    357 \begin{itemize}[topsep=3pt,itemsep=1pt]
     357\begin{itemize}[topsep=3pt,itemsep=0pt]
    358358\item
    359 language-level generators, coroutines and user-level threading, which respect the expectations of C programmers.
     359a set of fundamental execution properties that dictate which language-level control-flow features need to be supported,
     360
    360361\item
    361 monitor synchronization without barging, and the ability to safely acquiring multiple monitors \emph{simultaneously} (deadlock free), while seamlessly integrating these capabilities with all monitor synchronization mechanisms.
     362integration of these language-level control-flow features, while respecting the style and expectations of C programmers,
     363
    362364\item
    363 providing statically type-safe interfaces that integrate with the \CFA polymorphic type-system and other language features.
     365monitor synchronization without barging, and the ability to safely acquiring multiple monitors in a deadlock-free way, while seamlessly integrating these capabilities with all monitor synchronization mechanisms,
     366
     367\item
     368providing statically type-safe interfaces that integrate with the \CFA polymorphic type-system and other language features,
     369
    364370% \item
    365371% library extensions for executors, futures, and actors built on the basic mechanisms.
     372
    366373\item
    367 a runtime system with no spurious wakeup.
     374a runtime system without spurious wake-up and no performance loss,
     375
    368376\item
    369 a dynamic partitioning mechanism to segregate the execution environment for specialized requirements.
     377a dynamic partitioning mechanism to segregate groups of executing user and kernel threads performing specialized work, \eg web-server or compute engine, or requiring different scheduling, \eg NUMA or real-time.
     378
    370379% \item
    371 % a non-blocking I/O library
     380% a nonblocking I/O library
     381
    372382\item
    373 experimental results showing comparable performance of the new features with similar mechanisms in other programming languages.
     383experimental results showing comparable performance of the \CFA features with similar mechanisms in other languages.
    374384\end{itemize}
    375385
    376 Section~\ref{s:StatefulFunction} begins advanced control by introducing sequential functions that retain data and execution state between calls, which produces constructs @generator@ and @coroutine@.
    377 Section~\ref{s:Concurrency} begins concurrency, or how to create (fork) and destroy (join) a thread, which produces the @thread@ construct.
    378 Section~\ref{s:MutualExclusionSynchronization} discusses the two mechanisms to restricted nondeterminism when controlling shared access to resources (mutual exclusion) and timing relationships among threads (synchronization).
     386Section~\ref{s:FundamentalExecutionProperties} presents the compositional hierarchy of execution properties directing the design of control-flow features in \CFA.
     387Section~\ref{s:StatefulFunction} begins advanced control by introducing sequential functions that retain data and execution state between calls producing constructs @generator@ and @coroutine@.
     388Section~\ref{s:Concurrency} begins concurrency, or how to create (fork) and destroy (join) a thread producing the @thread@ construct.
     389Section~\ref{s:MutualExclusionSynchronization} discusses the two mechanisms to restricted nondeterminism when controlling shared access to resources, called mutual exclusion, and timing relationships among threads, called synchronization.
    379390Section~\ref{s:Monitor} shows how both mutual exclusion and synchronization are safely embedded in the @monitor@ and @thread@ constructs.
    380 Section~\ref{s:CFARuntimeStructure} describes the large-scale mechanism to structure (cluster) threads and virtual processors (kernel threads).
    381 Section~\ref{s:Performance} uses a series of microbenchmarks to compare \CFA threading with pthreads, Java OpenJDK-9, Go 1.12.6 and \uC 7.0.0.
     391Section~\ref{s:CFARuntimeStructure} describes the large-scale mechanism to structure threads and virtual processors (kernel threads).
     392Section~\ref{s:Performance} uses microbenchmarks to compare \CFA threading with pthreads, Java 11.0.6, Go 1.12.6, Rust 1.37.0, Python 3.7.6, Node.js v12.18.0, and \uC 7.0.0.
     393
     394
     395\section{Fundamental Execution Properties}
     396\label{s:FundamentalExecutionProperties}
     397
     398The features in a programming language should be composed of a set of fundamental properties rather than an ad hoc collection chosen by the designers.
     399To this end, the control-flow features created for \CFA are based on the fundamental properties of any language with function-stack control-flow (see also \uC~\cite[pp.~140-142]{uC++}).
     400The fundamental properties are execution state, thread, and mutual-exclusion/synchronization.
     401These independent properties can be used to compose different language features, forming a compositional hierarchy, where the combination of all three is the most advanced feature, called a thread.
     402While it is possible for a language to only provide threads for composing programs~\cite{Hermes90}, this unnecessarily complicates and makes inefficient solutions to certain classes of problems.
     403As is shown, each of the non-rejected composed language features solves a particular set of problems, and hence, has a defensible position in a programming language.
     404If a compositional feature is missing, a programmer has too few fundamental properties resulting in a complex and/or inefficient solution.
     405
     406In detail, the fundamental properties are:
     407\begin{description}[leftmargin=\parindent,topsep=3pt,parsep=0pt]
     408\item[\newterm{execution state}:]
     409It is the state information needed by a control-flow feature to initialize and manage both compute data and execution location(s), and de-initialize.
     410For example, calling a function initializes a stack frame including contained objects with constructors, manages local data in blocks and return locations during calls, and de-initializes the frame by running any object destructors and management operations.
     411State is retained in fixed-sized aggregate structures (objects) and dynamic-sized stack(s), often allocated in the heap(s) managed by the runtime system.
     412The lifetime of state varies with the control-flow feature, where longer life-time and dynamic size provide greater power but also increase usage complexity and cost.
     413Control-flow transfers among execution states in multiple ways, such as function call, context switch, asynchronous await, etc.
     414Because the programming language determines what constitutes an execution state, implicitly manages this state, and defines movement mechanisms among states, execution state is an elementary property of the semantics of a programming language.
     415% An execution-state is related to the notion of a process continuation \cite{Hieb90}.
     416
     417\item[\newterm{threading}:]
     418It is execution of code that occurs independently of other execution, where an individual thread's execution is sequential.
     419Multiple threads provide \emph{concurrent execution};
     420concurrent execution becomes parallel when run on multiple processing units, \eg hyper-threading, cores, or sockets.
     421A programmer needs mechanisms to create, block and unblock, and join with a thread, even if these basic mechanisms are supplied indirectly through high-level features.
     422
     423\item[\newterm{mutual-exclusion / synchronization (MES)}:]
     424It is the concurrency mechanism to perform an action without interruption and establish timing relationships among multiple threads.
     425We contented these two properties are independent, \ie mutual exclusion cannot provide synchronization and vice versa without introducing additional threads~\cite[\S~4]{Buhr05a}.
     426Limiting MES functionality results in contrived solutions and inefficiency on multicore von Neumann computers where shared memory is a foundational aspect of its design.
     427\end{description}
     428These properties are fundamental as they cannot be built from existing language features, \eg a basic programming language like C99~\cite{C99} cannot create new control-flow features, concurrency, or provide MES without (atomic) hardware mechanisms.
     429
     430
     431\subsection{Structuring execution properties}
     432
     433Programming languages seldom present the fundamental execution properties directly to programmers.
     434Instead, the properties are packaged into higher-level constructs that encapsulate details and provide safety to these low-level mechanisms.
     435Interestingly, language designers often pick and choose among these execution properties proving a varying subset of constructs.
     436
     437Table~\ref{t:ExecutionPropertyComposition} shows all combinations of the three fundamental execution properties available to language designers.
     438(When doing combination case-analysis, not all combinations are meaningful.)
     439The combinations of state, thread, and MES compose a hierarchy of control-flow features all of which have appeared in prior programming languages, where each of these languages have found the feature useful.
     440To understand the table, it is important to review the basic von Neumann execution requirement of at least one thread and execution state providing some form of call stack.
     441For table entries missing these minimal components, the property is borrowed from the invoker (caller).
     442Each entry in the table, numbered \textbf{1}--\textbf{12}, is discussed with respect to how the execution properties combine to generate a high-level language construct.
     443
     444\begin{table}
     445\caption{Execution property composition}
     446\centering
     447\label{t:ExecutionPropertyComposition}
     448\renewcommand{\arraystretch}{1.25}
     449%\setlength{\tabcolsep}{5pt}
     450\vspace*{-5pt}
     451\begin{tabular}{c|c||l|l}
     452\multicolumn{2}{c||}{Execution properties} & \multicolumn{2}{c}{Mutual exclusion / synchronization} \\
     453\hline
     454stateful                        & thread        & \multicolumn{1}{c|}{No} & \multicolumn{1}{c}{Yes} \\
     455\hline
     456\hline
     457No                                      & No            & \textbf{1}\ \ \ @struct@                              & \textbf{2}\ \ \ @mutex@ @struct@              \\
     458\hline
     459Yes (stackless)         & No            & \textbf{3}\ \ \ @generator@                   & \textbf{4}\ \ \ @mutex@ @generator@   \\
     460\hline
     461Yes (stackful)          & No            & \textbf{5}\ \ \ @coroutine@                   & \textbf{6}\ \ \ @mutex@ @coroutine@   \\
     462\hline
     463No                                      & Yes           & \textbf{7}\ \ \ {\color{red}rejected} & \textbf{8}\ \ \ {\color{red}rejected} \\
     464\hline
     465Yes (stackless)         & Yes           & \textbf{9}\ \ \ {\color{red}rejected} & \textbf{10}\ \ \ {\color{red}rejected} \\
     466\hline
     467Yes (stackful)          & Yes           & \textbf{11}\ \ \ @thread@                             & \textbf{12}\ \ @mutex@ @thread@               \\
     468\end{tabular}
     469\vspace*{-8pt}
     470\end{table}
     471
     472Case 1 is a structure where access functions borrow local state (stack frame/activation) and thread from the invoker and retain this state across \emph{callees}, \ie function local-variables are retained on the borrowed stack during calls.
     473Structures are a foundational mechanism for data organization, and access functions provide interface abstraction and code sharing in all programming languages.
     474Case 2 is case 1 with thread safety to a structure's state where access functions provide serialization (mutual exclusion) and scheduling among calling threads (synchronization).
     475A @mutex@ structure, often called a \newterm{monitor}, provides a high-level interface for race-free access of shared data in concurrent programming languages.
     476Case 3 is case 1 where the structure can implicitly retain execution state and access functions use this execution state to resume/suspend across \emph{callers}, but resume/suspend does not retain a function's local state.
     477A stackless structure, often called a \newterm{generator} or \emph{iterator}, is \newterm{stackless} because it still borrows the caller's stack and thread, but the stack is used only to preserve state across its callees not callers.
     478Generators provide the first step toward directly solving problems like finite-state machines (FSMs) that retain data and execution state between calls, whereas normal functions restart on each call.
     479Case 4 is cases 2 and 3 with thread safety during execution of the generator's access functions.
     480A @mutex@ generator extends generators into the concurrent domain.
     481Cases 5 and 6 are like cases 3 and 4 where the structure is extended with an implicit separate stack, so only the thread is borrowed by access functions.
     482A stackful generator, often called a \newterm{coroutine}, is \newterm{stackful} because resume/suspend now context switch to/from the caller's and coroutine's stack.
     483A coroutine extends the state retained between calls beyond the generator's structure to arbitrary call depth in the access functions.
     484Cases 7, 8, 9 and 10 are rejected because a new thread must have its own stack, where the thread begins and stack frames are stored for calls, \ie it is unrealistic for a thread to borrow a stack.
     485For cases 9 and 10, the stackless frame is not growable, precluding accepting nested calls, making calls, blocking as it requires calls, or preemption as it requires pushing an interrupt frame, all of which compound to require an unknown amount of execution state.
     486Hence, if this kind of uninterruptable thread exists, it must execute to completion, \ie computation only, which severely restricts runtime management.
     487Cases 11 and 12 are a stackful thread with and without safe access to shared state.
     488A thread is the language mechanism to start another thread of control in a program with growable execution state for call/return execution.
     489In general, language constructs with more execution properties increase the cost of creation and execution along with complexity of usage.
     490
     491Given the execution-properties taxonomy, programmers now ask three basic questions: is state necessary across callers and how much, is a separate thread necessary, is thread safety necessary.
     492Table~\ref{t:ExecutionPropertyComposition} then suggests the optimal language feature needed for implementing a programming problem.
     493The following sections describe how \CFA fills in \emph{all} the nonrejected table entries with language features, while other programming languages may only provide a subset of the table.
     494
     495
     496\subsection{Design requirements}
     497
     498The following design requirements largely stem from building \CFA on top of C.
     499\begin{itemize}[topsep=3pt,parsep=0pt]
     500\item
     501All communication must be statically type checkable for early detection of errors and efficient code generation.
     502This requirement is consistent with the fact that C is a statically typed programming language.
     503
     504\item
     505Direct interaction among language features must be possible allowing any feature to be selected without restricting comm\-unication.
     506For example, many concurrent languages do not provide direct communication calls among threads, \ie threads only communicate indirectly through monitors, channels, messages, and/or futures.
     507Indirect communication increases the number of objects, consuming more resources, and requires additional synchronization and possibly data transfer.
     508
     509\item
     510All communication is performed using function calls, \ie data are transmitted from argument to parameter and results are returned from function calls.
     511Alternative forms of communication, such as call-backs, message passing, channels, or communication ports, step outside of C's normal form of communication.
     512
     513\item
     514All stateful features must follow the same declaration scopes and lifetimes as other language data.
     515For C that means at program startup, during block and function activation, and on demand using dynamic allocation.
     516
     517\item
     518MES must be available implicitly in language constructs, \eg Java built-in monitors, as well as explicitly for specialized requirements, \eg @java.util.concurrent@, because requiring programmers to build MES using low-level locks often leads to incorrect programs.
     519Furthermore, reducing synchronization scope by encapsulating it within language constructs further reduces errors in concurrent programs.
     520
     521\item
     522Both synchronous and asynchronous communication are needed.
     523However, we believe the best way to provide asynchrony, such as call-buffering/chaining and/or returning futures~\cite{multilisp}, is building it from expressive synchronous features.
     524
     525\item
     526Synchronization must be able to control the service order of requests including prioritizing selection from different kinds of outstanding requests, and postponing a request for an unspecified time while continuing to accept new requests.
     527Otherwise, certain concurrency problems are difficult, \eg web server, disk scheduling, and the amount of concurrency is inhibited~\cite{Gentleman81}.
     528\end{itemize}
     529We have satisfied these requirements in \CFA while maintaining backwards compatibility with the huge body of legacy C programs.
     530% In contrast, other new programming languages must still access C programs (\eg operating-system service routines), but do so through fragile C interfaces.
     531
     532
     533\subsection{Asynchronous await / call}
     534
     535Asynchronous await/call is a caller mechanism for structuring programs and/or increasing concurrency, where the caller (client) postpones an action into the future, which is subsequently executed by a callee (server).
     536The caller detects the action's completion through a \newterm{future} or \newterm{promise}.
     537The benefit is asynchronous caller execution with respect to the callee until future resolution.
     538For single-threaded languages like JavaScript, an asynchronous call passes a callee action, which is queued in the event-engine, and continues execution with a promise.
     539When the caller needs the promise to be fulfilled, it executes @await@.
     540A promise-completion call-back can be part of the callee action or the caller is rescheduled;
     541in either case, the call back is executed after the promise is fulfilled.
     542While asynchronous calls generate new callee (server) events, we contend this mechanism is insufficient for advanced control-flow mechanisms like generators or coroutines, which are discussed next.
     543Specifically, control between caller and callee occurs indirectly through the event-engine precluding direct handoff and cycling among events, and requires complex resolution of a control promise and data.
     544Note, @async-await@ is just syntactic-sugar over the event engine so it does not solve these deficiencies.
     545For multithreaded languages like Java, the asynchronous call queues a callee action with an executor (server), which subsequently executes the work by a thread in the executor thread-pool.
     546The problem is when concurrent work-units need to interact and/or block as this effects the executor by stopping threads.
     547While it is possible to extend this approach to support the necessary mechanisms, \eg message passing in Actors, we show monitors and threads provide an equally competitive approach that does not deviate from normal call communication and can be used to build asynchronous call, as is done in Java.
    382548
    383549
     
    385551\label{s:StatefulFunction}
    386552
    387 The stateful function is an old idea~\cite{Conway63,Marlin80} that is new again~\cite{C++20Coroutine19}, where execution is temporarily suspended and later resumed, \eg plugin, device driver, finite-state machine.
    388 Hence, a stateful function may not end when it returns to its caller, allowing it to be restarted with the data and execution location present at the point of suspension.
    389 This capability is accomplished by retaining a data/execution \emph{closure} between invocations.
    390 If the closure is fixed size, we call it a \emph{generator} (or \emph{stackless}), and its control flow is restricted, \eg suspending outside the generator is prohibited.
    391 If the closure is variable size, we call it a \emph{coroutine} (or \emph{stackful}), and as the names implies, often implemented with a separate stack with no programming restrictions.
    392 Hence, refactoring a stackless coroutine may require changing it to stackful.
    393 A foundational property of all \emph{stateful functions} is that resume/suspend \emph{do not} cause incremental stack growth, \ie resume/suspend operations are remembered through the closure not the stack.
    394 As well, activating a stateful function is \emph{asymmetric} or \emph{symmetric}, identified by resume/suspend (no cycles) and resume/resume (cycles).
    395 A fixed closure activated by modified call/return is faster than a variable closure activated by context switching.
    396 Additionally, any storage management for the closure (especially in unmanaged languages, \ie no garbage collection) must also be factored into design and performance.
    397 Therefore, selecting between stackless and stackful semantics is a tradeoff between programming requirements and performance, where stackless is faster and stackful is more general.
    398 Note, creation cost is amortized across usage, so activation cost is usually the dominant factor.
     553A \emph{stateful function} has the ability to remember state between calls, where state can be either data or execution, \eg plugin, device driver, FSM.
     554A simple technique to retain data state between calls is @static@ declarations within a function, which is often implemented by hoisting the declarations to the global scope but hiding the names within the function using name mangling.
     555However, each call starts the function at the top making it difficult to determine the last point of execution in an algorithm, and requiring multiple flag variables and testing to reestablish the continuation point.
     556Hence, the next step of generalizing function state is implicitly remembering the return point between calls and reentering the function at this point rather than the top, called \emph{generators}\,/\,\emph{iterators} or \emph{stackless coroutines}.
     557For example, a Fibonacci generator retains data and execution state allowing it to remember prior values needed to generate the next value and the location in the algorithm to compute that value.
     558The next step of generalization is instantiating the function to allow multiple named instances, \eg multiple Fibonacci generators, where each instance has its own state, and hence, can generate an independent sequence of values.
     559Note, a subset of generator state is a function \emph{closure}, \ie the technique of capturing lexical references when returning a nested function.
     560A further generalization is adding a stack to a generator's state, called a \emph{coroutine}, so it can suspend outside of itself, \eg call helper functions to arbitrary depth before suspending back to its resumer without unwinding these calls.
     561For example, a coroutine iterator for a binary tree can stop the traversal at the visit point (pre, infix, post traversal), return the node value to the caller, and then continue the recursive traversal from the current node on the next call.
     562
     563There are two styles of activating a stateful function, \emph{asymmetric} or \emph{symmetric}, identified by resume/suspend (no cycles) and resume/resume (cycles).
     564These styles \emph{do not} cause incremental stack growth, \eg a million resume/suspend or resume/resume cycles do not remember each cycle just the last resumer for each cycle.
     565Selecting between stackless/stackful semantics and asymmetric/symmetric style is a tradeoff between programming requirements, performance, and design, where stackless is faster and smaller using modified call/return between closures, stackful is more general but slower and larger using context switching between distinct stacks, and asymmetric is simpler control-flow than symmetric.
     566Additionally, storage management for the closure/stack must be factored into design and performance, especially in unmanaged languages without garbage collection.
     567Note, creation cost (closure/stack) is amortized across usage, so activation cost (resume/suspend) is usually the dominant factor.
     568
     569% The stateful function is an old idea~\cite{Conway63,Marlin80} that is new again~\cite{C++20Coroutine19}, where execution is temporarily suspended and later resumed, \eg plugin, device driver, finite-state machine.
     570% Hence, a stateful function may not end when it returns to its caller, allowing it to be restarted with the data and execution location present at the point of suspension.
     571% If the closure is fixed size, we call it a \emph{generator} (or \emph{stackless}), and its control flow is restricted, \eg suspending outside the generator is prohibited.
     572% If the closure is variable size, we call it a \emph{coroutine} (or \emph{stackful}), and as the names implies, often implemented with a separate stack with no programming restrictions.
     573% Hence, refactoring a stackless coroutine may require changing it to stackful.
     574% A foundational property of all \emph{stateful functions} is that resume/suspend \emph{do not} cause incremental stack growth, \ie resume/suspend operations are remembered through the closure not the stack.
     575% As well, activating a stateful function is \emph{asymmetric} or \emph{symmetric}, identified by resume/suspend (no cycles) and resume/resume (cycles).
     576% A fixed closure activated by modified call/return is faster than a variable closure activated by context switching.
     577% Additionally, any storage management for the closure (especially in unmanaged languages, \ie no garbage collection) must also be factored into design and performance.
     578% Therefore, selecting between stackless and stackful semantics is a tradeoff between programming requirements and performance, where stackless is faster and stackful is more general.
     579% nppNote, creation cost is amortized across usage, so activation cost is usually the dominant factor.
     580
     581For example, Python presents asymmetric generators as a function object, \uC presents symmetric coroutines as a \lstinline[language=C++]|class|-like object, and many languages present threading using function pointers, @pthreads@~\cite{Butenhof97}, \Csharp~\cite{Csharp}, Go~\cite{Go}, and Scala~\cite{Scala}.
     582\begin{center}
     583\begin{tabular}{@{}l|l|l@{}}
     584\multicolumn{1}{@{}c|}{Python asymmetric generator} & \multicolumn{1}{c|}{\uC symmetric coroutine} & \multicolumn{1}{c@{}}{Pthreads thread} \\
     585\hline
     586\begin{python}
     587`def Gen():` $\LstCommentStyle{\color{red}// function}$
     588        ... yield val ...
     589gen = Gen()
     590for i in range( 10 ):
     591        print( next( gen ) )
     592\end{python}
     593&
     594\begin{uC++}
     595`_Coroutine Cycle {` $\LstCommentStyle{\color{red}// class}$
     596        Cycle * p;
     597        void main() { p->cycle(); }
     598        void cycle() { resume(); }  `};`
     599Cycle c1, c2; c1.p=&c2; c2.p=&c1; c1.cycle();
     600\end{uC++}
     601&
     602\begin{cfa}
     603void * `rtn`( void * arg ) { ... }
     604int i = 3, rc;
     605pthread_t t; $\C{// thread id}$
     606$\LstCommentStyle{\color{red}// function pointer}$
     607rc=pthread_create(&t, `rtn`, (void *)i);
     608\end{cfa}
     609\end{tabular}
     610\end{center}
     611\CFA's preferred presentation model for generators/coroutines/threads is a hybrid of functions and classes, giving an object-oriented flavor.
     612Essentially, the generator/coroutine/thread function is semantically coupled with a generator/coroutine/thread custom type via the type's name.
     613The custom type solves several issues, while accessing the underlying mechanisms used by the custom types is still allowed for flexibility reasons.
     614Each custom type is discussed in detail in the following sections.
     615
     616
     617\subsection{Generator}
     618
     619Stackless generators (Table~\ref{t:ExecutionPropertyComposition} case 3) have the potential to be very small and fast, \ie as small and fast as function call/return for both creation and execution.
     620The \CFA goal is to achieve this performance target, possibly at the cost of some semantic complexity.
     621A series of different kinds of generators and their implementation demonstrate how this goal is accomplished.\footnote{
     622The \CFA operator syntax uses \lstinline|?| to denote operands, which allows precise definitions for pre, post, and infix operators, \eg \lstinline|?++|, \lstinline|++?|, and \lstinline|?+?|, in addition \lstinline|?\{\}| denotes a constructor, as in \lstinline|foo `f` = `\{`...`\}`|, \lstinline|^?\{\}| denotes a destructor, and \lstinline|?()| is \CC function call \lstinline|operator()|.
     623Operator \lstinline+|+ is overloaded for printing, like bit-shift \lstinline|<<| in \CC.
     624The \CFA \lstinline|with| clause opens an aggregate scope making its fields directly accessible, like Pascal \lstinline|with|, but using parallel semantics;
     625multiple aggregates may be opened.
     626\CFA has rebindable references \lstinline|int i, & ip = i, j; `&ip = &j;`| and nonrebindable references \lstinline|int i, & `const` ip = i, j; `&ip = &j;` // disallowed|.
     627}%
    399628
    400629\begin{figure}
     
    410639
    411640
     641
     642
    412643        int fn = f->fn; f->fn = f->fn1;
    413644                f->fn1 = f->fn + fn;
    414645        return fn;
    415 
    416646}
    417647int main() {
     
    432662void `main(Fib & fib)` with(fib) {
    433663
     664
    434665        [fn1, fn] = [1, 0];
    435666        for () {
     
    451682\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    452683typedef struct {
    453         int fn1, fn;  void * `next`;
     684        int `restart`, fn1, fn;
    454685} Fib;
    455 #define FibCtor { 1, 0, NULL }
     686#define FibCtor { `0`, 1, 0 }
    456687Fib * comain( Fib * f ) {
    457         if ( f->next ) goto *f->next;
    458         f->next = &&s1;
     688        `static void * states[] = {&&s0, &&s1};`
     689        `goto *states[f->restart];`
     690  s0: f->`restart` = 1;
    459691        for ( ;; ) {
    460692                return f;
    461693          s1:; int fn = f->fn + f->fn1;
    462                         f->fn1 = f->fn; f->fn = fn;
     694                f->fn1 = f->fn; f->fn = fn;
    463695        }
    464696}
     
    472704\end{lrbox}
    473705
    474 \subfloat[C asymmetric generator]{\label{f:CFibonacci}\usebox\myboxA}
     706\subfloat[C]{\label{f:CFibonacci}\usebox\myboxA}
    475707\hspace{3pt}
    476708\vrule
    477709\hspace{3pt}
    478 \subfloat[\CFA asymmetric generator]{\label{f:CFAFibonacciGen}\usebox\myboxB}
     710\subfloat[\CFA]{\label{f:CFAFibonacciGen}\usebox\myboxB}
    479711\hspace{3pt}
    480712\vrule
    481713\hspace{3pt}
    482 \subfloat[C generator implementation]{\label{f:CFibonacciSim}\usebox\myboxC}
    483 \caption{Fibonacci (output) asymmetric generator}
     714\subfloat[C generated code for \CFA version]{\label{f:CFibonacciSim}\usebox\myboxC}
     715\caption{Fibonacci output asymmetric generator}
    484716\label{f:FibonacciAsymmetricGenerator}
    485717
     
    493725};
    494726void ?{}( Fmt & fmt ) { `resume(fmt);` } // constructor
    495 void ^?{}( Fmt & f ) with(f) { $\C[1.75in]{// destructor}$
     727void ^?{}( Fmt & f ) with(f) { $\C[2.25in]{// destructor}$
    496728        if ( g != 0 || b != 0 ) sout | nl; }
    497729void `main( Fmt & f )` with(f) {
     
    499731                for ( ; g < 5; g += 1 ) { $\C{// groups}$
    500732                        for ( ; b < 4; b += 1 ) { $\C{// blocks}$
    501                                 `suspend;` $\C{// wait for character}$
    502                                 while ( ch == '\n' ) `suspend;` // ignore
    503                                 sout | ch;                                              // newline
    504                         } sout | " ";  // block spacer
    505                 } sout | nl; // group newline
     733                                do { `suspend;` $\C{// wait for character}$
     734                                while ( ch == '\n' ); // ignore newline
     735                                sout | ch;                      $\C{// print character}$
     736                        } sout | " ";  $\C{// block separator}$
     737                } sout | nl; $\C{// group separator}$
    506738        }
    507739}
     
    521753\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    522754typedef struct {
    523         void * next;
     755        int `restart`, g, b;
    524756        char ch;
    525         int g, b;
    526757} Fmt;
    527758void comain( Fmt * f ) {
    528         if ( f->next ) goto *f->next;
    529         f->next = &&s1;
     759        `static void * states[] = {&&s0, &&s1};`
     760        `goto *states[f->restart];`
     761  s0: f->`restart` = 1;
    530762        for ( ;; ) {
    531763                for ( f->g = 0; f->g < 5; f->g += 1 ) {
    532764                        for ( f->b = 0; f->b < 4; f->b += 1 ) {
    533                                 return;
    534                           s1:;  while ( f->ch == '\n' ) return;
     765                                do { return;  s1: ;
     766                                } while ( f->ch == '\n' );
    535767                                printf( "%c", f->ch );
    536768                        } printf( " " );
     
    539771}
    540772int main() {
    541         Fmt fmt = { NULL };  comain( &fmt ); // prime
     773        Fmt fmt = { `0` };  comain( &fmt ); // prime
    542774        for ( ;; ) {
    543775                scanf( "%c", &fmt.ch );
     
    550782\end{lrbox}
    551783
    552 \subfloat[\CFA asymmetric generator]{\label{f:CFAFormatGen}\usebox\myboxA}
    553 \hspace{3pt}
     784\subfloat[\CFA]{\label{f:CFAFormatGen}\usebox\myboxA}
     785\hspace{35pt}
    554786\vrule
    555787\hspace{3pt}
    556 \subfloat[C generator simulation]{\label{f:CFormatSim}\usebox\myboxB}
     788\subfloat[C generated code for \CFA version]{\label{f:CFormatGenImpl}\usebox\myboxB}
    557789\hspace{3pt}
    558 \caption{Formatter (input) asymmetric generator}
     790\caption{Formatter input asymmetric generator}
    559791\label{f:FormatterAsymmetricGenerator}
    560792\end{figure}
    561793
    562 Stateful functions appear as generators, coroutines, and threads, where presentations are based on function objects or pointers~\cite{Butenhof97, C++14, MS:VisualC++, BoostCoroutines15}.
    563 For example, Python presents generators as a function object:
    564 \begin{python}
    565 def Gen():
    566         ... `yield val` ...
    567 gen = Gen()
    568 for i in range( 10 ):
    569         print( next( gen ) )
    570 \end{python}
    571 Boost presents coroutines in terms of four functor object-types:
    572 \begin{cfa}
    573 asymmetric_coroutine<>::pull_type
    574 asymmetric_coroutine<>::push_type
    575 symmetric_coroutine<>::call_type
    576 symmetric_coroutine<>::yield_type
    577 \end{cfa}
    578 and many languages present threading using function pointers, @pthreads@~\cite{Butenhof97}, \Csharp~\cite{Csharp}, Go~\cite{Go}, and Scala~\cite{Scala}, \eg pthreads:
    579 \begin{cfa}
    580 void * rtn( void * arg ) { ... }
    581 int i = 3, rc;
    582 pthread_t t; $\C{// thread id}$
    583 `rc = pthread_create( &t, rtn, (void *)i );` $\C{// create and initialized task, type-unsafe input parameter}$
    584 \end{cfa}
    585 % void mycor( pthread_t cid, void * arg ) {
    586 %       int * value = (int *)arg;                               $\C{// type unsafe, pointer-size only}$
    587 %       // thread body
    588 % }
    589 % int main() {
    590 %       int input = 0, output;
    591 %       coroutine_t cid = coroutine_create( &mycor, (void *)&input ); $\C{// type unsafe, pointer-size only}$
    592 %       coroutine_resume( cid, (void *)input, (void **)&output ); $\C{// type unsafe, pointer-size only}$
    593 % }
    594 \CFA's preferred presentation model for generators/coroutines/threads is a hybrid of objects and functions, with an object-oriented flavour.
    595 Essentially, the generator/coroutine/thread function is semantically coupled with a generator/coroutine/thread custom type.
    596 The custom type solves several issues, while accessing the underlying mechanisms used by the custom types is still allowed.
    597 
    598 
    599 \subsection{Generator}
    600 
    601 Stackless generators have the potential to be very small and fast, \ie as small and fast as function call/return for both creation and execution.
    602 The \CFA goal is to achieve this performance target, possibly at the cost of some semantic complexity.
    603 A series of different kinds of generators and their implementation demonstrate how this goal is accomplished.
    604 
    605 Figure~\ref{f:FibonacciAsymmetricGenerator} shows an unbounded asymmetric generator for an infinite sequence of Fibonacci numbers written in C and \CFA, with a simple C implementation for the \CFA version.
     794Figure~\ref{f:FibonacciAsymmetricGenerator} shows an unbounded asymmetric generator for an infinite sequence of Fibonacci numbers written left to right in C, \CFA, and showing the underlying C implementation for the \CFA version.
    606795This generator is an \emph{output generator}, producing a new result on each resumption.
    607796To compute Fibonacci, the previous two values in the sequence are retained to generate the next value, \ie @fn1@ and @fn@, plus the execution location where control restarts when the generator is resumed, \ie top or middle.
    608 An additional requirement is the ability to create an arbitrary number of generators (of any kind), \ie retaining one state in global variables is insufficient;
     797An additional requirement is the ability to create an arbitrary number of generators of any kind, \ie retaining one state in global variables is insufficient;
    609798hence, state is retained in a closure between calls.
    610799Figure~\ref{f:CFibonacci} shows the C approach of manually creating the closure in structure @Fib@, and multiple instances of this closure provide multiple Fibonacci generators.
    611800The C version only has the middle execution state because the top execution state is declaration initialization.
    612801Figure~\ref{f:CFAFibonacciGen} shows the \CFA approach, which also has a manual closure, but replaces the structure with a custom \CFA @generator@ type.
    613 This generator type is then connected to a function that \emph{must be named \lstinline|main|},\footnote{
    614 The name \lstinline|main| has special meaning in C, specifically the function where a program starts execution.
    615 Hence, overloading this name for other starting points (generator/coroutine/thread) is a logical extension.}
    616 called a \emph{generator main},which takes as its only parameter a reference to the generator type.
     802Each generator type must have a function named \lstinline|main|,
     803% \footnote{
     804% The name \lstinline|main| has special meaning in C, specifically the function where a program starts execution.
     805% Leveraging starting semantics to this name for generator/coroutine/thread is a logical extension.}
     806called a \emph{generator main} (leveraging the starting semantics for program @main@ in C), which is connected to the generator type via its single reference parameter.
    617807The generator main contains @suspend@ statements that suspend execution without ending the generator versus @return@.
    618 For the Fibonacci generator-main,\footnote{
    619 The \CFA \lstinline|with| opens an aggregate scope making its fields directly accessible, like Pascal \lstinline|with|, but using parallel semantics.
    620 Multiple aggregates may be opened.}
    621 the top initialization state appears at the start and the middle execution state is denoted by statement @suspend@.
     808For the Fibonacci generator-main, the top initialization state appears at the start and the middle execution state is denoted by statement @suspend@.
    622809Any local variables in @main@ \emph{are not retained} between calls;
    623810hence local variables are only for temporary computations \emph{between} suspends.
     
    627814Resuming an ended (returned) generator is undefined.
    628815Function @resume@ returns its argument generator so it can be cascaded in an expression, in this case to print the next Fibonacci value @fn@ computed in the generator instance.
    629 Figure~\ref{f:CFibonacciSim} shows the C implementation of the \CFA generator only needs one additional field, @next@, to handle retention of execution state.
    630 The computed @goto@ at the start of the generator main, which branches after the previous suspend, adds very little cost to the resume call.
    631 Finally, an explicit generator type provides both design and performance benefits, such as multiple type-safe interface functions taking and returning arbitrary types.\footnote{
    632 The \CFA operator syntax uses \lstinline|?| to denote operands, which allows precise definitions for pre, post, and infix operators, \eg \lstinline|++?|, \lstinline|?++|, and \lstinline|?+?|, in addition \lstinline|?\{\}| denotes a constructor, as in \lstinline|foo `f` = `\{`...`\}`|, \lstinline|^?\{\}| denotes a destructor, and \lstinline|?()| is \CC function call \lstinline|operator()|.
    633 }%
     816Figure~\ref{f:CFibonacciSim} shows the C implementation of the \CFA asymmetric generator.
     817Only one execution-state field, @restart@, is needed to subscript the suspension points in the generator.
     818At the start of the generator main, the @static@ declaration, @states@, is initialized to the N suspend points in the generator, where operator @&&@ dereferences or references a label~\cite{gccValueLabels}.
     819Next, the computed @goto@ selects the last suspend point and branches to it.
     820The cost of setting @restart@ and branching via the computed @goto@ adds very little cost to the suspend and resume calls.
     821
     822An advantage of the \CFA explicit generator type is the ability to allow multiple type-safe interface functions taking and returning arbitrary types.
    634823\begin{cfa}
    635824int ?()( Fib & fib ) { return `resume( fib )`.fn; } $\C[3.9in]{// function-call interface}$
    636 int ?()( Fib & fib, int N ) { for ( N - 1 ) `fib()`; return `fib()`; } $\C{// use function-call interface to skip N values}$
    637 double ?()( Fib & fib ) { return (int)`fib()` / 3.14159; } $\C{// different return type, cast prevents recursive call}\CRT$
    638 sout | (int)f1() | (double)f1() | f2( 2 ); // alternative interface, cast selects call based on return type, step 2 values
     825int ?()( Fib & fib, int N ) { for ( N - 1 ) `fib()`; return `fib()`; } $\C{// add parameter to skip N values}$
     826double ?()( Fib & fib ) { return (int)`fib()` / 3.14159; } $\C{// different return type, cast prevents recursive call}$
     827Fib f;  int i;  double d;
     828i = f();  i = f( 2 );  d = f();                                         $\C{// alternative interfaces}\CRT$
    639829\end{cfa}
    640830Now, the generator can be a separately compiled opaque-type only accessed through its interface functions.
    641831For contrast, Figure~\ref{f:PythonFibonacci} shows the equivalent Python Fibonacci generator, which does not use a generator type, and hence only has a single interface, but an implicit closure.
    642832
    643 Having to manually create the generator closure by moving local-state variables into the generator type is an additional programmer burden.
    644 (This restriction is removed by the coroutine in Section~\ref{s:Coroutine}.)
    645 This requirement follows from the generality of variable-size local-state, \eg local state with a variable-length array requires dynamic allocation because the array size is unknown at compile time.
     833\begin{figure}
     834%\centering
     835\newbox\myboxA
     836\begin{lrbox}{\myboxA}
     837\begin{python}[aboveskip=0pt,belowskip=0pt]
     838def Fib():
     839        fn1, fn = 0, 1
     840        while True:
     841                `yield fn1`
     842                fn1, fn = fn, fn1 + fn
     843f1 = Fib()
     844f2 = Fib()
     845for i in range( 10 ):
     846        print( next( f1 ), next( f2 ) )
     847
     848
     849
     850
     851
     852
     853
     854
     855
     856
     857\end{python}
     858\end{lrbox}
     859
     860\newbox\myboxB
     861\begin{lrbox}{\myboxB}
     862\begin{python}[aboveskip=0pt,belowskip=0pt]
     863def Fmt():
     864        try:
     865                while True:                                             $\C[2.5in]{\# until destructor call}$
     866                        for g in range( 5 ):            $\C{\# groups}$
     867                                for b in range( 4 ):    $\C{\# blocks}$
     868                                        while True:
     869                                                ch = (yield)    $\C{\# receive from send}$
     870                                                if '\n' not in ch: $\C{\# ignore newline}$
     871                                                        break
     872                                        print( ch, end='' )     $\C{\# print character}$
     873                                print( '  ', end='' )   $\C{\# block separator}$
     874                        print()                                         $\C{\# group separator}$
     875        except GeneratorExit:                           $\C{\# destructor}$
     876                if g != 0 | b != 0:                             $\C{\# special case}$
     877                        print()
     878fmt = Fmt()
     879`next( fmt )`                                                   $\C{\# prime, next prewritten}$
     880for i in range( 41 ):
     881        `fmt.send( 'a' );`                                      $\C{\# send to yield}$
     882\end{python}
     883\end{lrbox}
     884
     885\hspace{30pt}
     886\subfloat[Fibonacci]{\label{f:PythonFibonacci}\usebox\myboxA}
     887\hspace{3pt}
     888\vrule
     889\hspace{3pt}
     890\subfloat[Formatter]{\label{f:PythonFormatter}\usebox\myboxB}
     891\caption{Python generator}
     892\label{f:PythonGenerator}
     893\end{figure}
     894
     895Having to manually create the generator closure by moving local-state variables into the generator type is an additional programmer burden (removed by the coroutine in Section~\ref{s:Coroutine}).
     896This manual requirement follows from the generality of allowing variable-size local-state, \eg local state with a variable-length array requires dynamic allocation as the array size is unknown at compile time.
    646897However, dynamic allocation significantly increases the cost of generator creation/destruction and is a showstopper for embedded real-time programming.
    647898But more importantly, the size of the generator type is tied to the local state in the generator main, which precludes separate compilation of the generator main, \ie a generator must be inlined or local state must be dynamically allocated.
    648 With respect to safety, we believe static analysis can discriminate local state from temporary variables in a generator, \ie variable usage spanning @suspend@, and generate a compile-time error.
    649 Finally, our current experience is that most generator problems have simple data state, including local state, but complex execution state, so the burden of creating the generator type is small.
    650 As well, C programmers are not afraid of this kind of semantic programming requirement, if it results in very small, fast generators.
     899With respect to safety, we believe static analysis can discriminate persistent generator state from temporary generator-main state and raise a compile-time error for temporary usage spanning suspend points.
     900Our experience using generators is that the problems have simple data state, including local state, but complex execution state, so the burden of creating the generator type is small.
     901As well, C programmers are not afraid of this kind of semantic programming requirement, if it results in very small and fast generators.
    651902
    652903Figure~\ref{f:CFAFormatGen} shows an asymmetric \newterm{input generator}, @Fmt@, for restructuring text into groups of characters of fixed-size blocks, \ie the input on the left is reformatted into the output on the right, where newlines are ignored.
     
    669920The example takes advantage of resuming a generator in the constructor to prime the loops so the first character sent for formatting appears inside the nested loops.
    670921The destructor provides a newline, if formatted text ends with a full line.
    671 Figure~\ref{f:CFormatSim} shows the C implementation of the \CFA input generator with one additional field and the computed @goto@.
    672 For contrast, Figure~\ref{f:PythonFormatter} shows the equivalent Python format generator with the same properties as the Fibonacci generator.
    673 
    674 Figure~\ref{f:DeviceDriverGen} shows a \emph{killer} asymmetric generator, a device-driver, because device drivers caused 70\%-85\% of failures in Windows/Linux~\cite{Swift05}.
    675 Device drives follow the pattern of simple data state but complex execution state, \ie finite state-machine (FSM) parsing a protocol.
    676 For example, the following protocol:
     922Figure~\ref{f:CFormatGenImpl} shows the C implementation of the \CFA input generator with one additional field and the computed @goto@.
     923For contrast, Figure~\ref{f:PythonFormatter} shows the equivalent Python format generator with the same properties as the \CFA format generator.
     924
     925% https://dl-acm-org.proxy.lib.uwaterloo.ca/
     926
     927An important application for the asymmetric generator is a device-driver, because device drivers are a significant source of operating-system errors: 85\% in Windows XP~\cite[p.~78]{Swift05} and 51.6\% in Linux~\cite[p.~1358,]{Xiao19}. %\cite{Palix11}
     928Swift \etal~\cite[p.~86]{Swift05} restructure device drivers using the Extension Procedure Call (XPC) within the kernel via functions @nooks_driver_call@ and @nooks_kernel_call@, which have coroutine properties context switching to separate stacks with explicit hand-off calls;
     929however, the calls do not retain execution state, and hence always start from the top.
     930The alternative approach for implementing device drivers is using stack-ripping.
     931However, Adya \etal~\cite{Adya02} argue against stack ripping in Section 3.2 and suggest a hybrid approach in Section 4 using cooperatively scheduled \emph{fibers}, which is coroutining.
     932
     933Figure~\ref{f:DeviceDriverGen} shows the generator advantages in implementing a simple network device-driver with the following protocol:
    677934\begin{center}
    678935\ldots\, STX \ldots\, message \ldots\, ESC ETX \ldots\, message \ldots\, ETX 2-byte crc \ldots
    679936\end{center}
    680 is a network message beginning with the control character STX, ending with an ETX, and followed by a 2-byte cyclic-redundancy check.
     937where the network message begins with the control character STX, ends with an ETX, and is followed by a two-byte cyclic-redundancy check.
    681938Control characters may appear in a message if preceded by an ESC.
    682939When a message byte arrives, it triggers an interrupt, and the operating system services the interrupt by calling the device driver with the byte read from a hardware register.
    683 The device driver returns a status code of its current state, and when a complete message is obtained, the operating system knows the message is in the message buffer.
    684 Hence, the device driver is an input/output generator.
    685 
    686 Note, the cost of creating and resuming the device-driver generator, @Driver@, is virtually identical to call/return, so performance in an operating-system kernel is excellent.
    687 As well, the data state is small, where variables @byte@ and @msg@ are communication variables for passing in message bytes and returning the message, and variables @lnth@, @crc@, and @sum@ are local variable that must be retained between calls and are manually hoisted into the generator type.
    688 % Manually, detecting and hoisting local-state variables is easy when the number is small.
    689 In contrast, the execution state is large, with one @resume@ and seven @suspend@s.
    690 Hence, the key benefits of the generator are correctness, safety, and maintenance because the execution states are transcribed directly into the programming language rather than using a table-driven approach.
    691 Because FSMs can be complex and frequently occur in important domains, direct generator support is important in a system programming language.
     940The device driver returns a status code of its current state, and when a complete message is obtained, the operating system reads the message accumulated in the supplied buffer.
     941Hence, the device driver is an input/output generator, where the cost of resuming the device-driver generator is the same as call and return, so performance in an operating-system kernel is excellent.
     942The key benefits of using a generator are correctness, safety, and maintenance because the execution states are transcribed directly into the programming language rather than table lookup or stack ripping.
     943% The conclusion is that FSMs are complex and occur in important domains, so direct generator support is important in a system programming language.
    692944
    693945\begin{figure}
    694946\centering
    695 \newbox\myboxA
    696 \begin{lrbox}{\myboxA}
    697 \begin{python}[aboveskip=0pt,belowskip=0pt]
    698 def Fib():
    699         fn1, fn = 0, 1
    700         while True:
    701                 `yield fn1`
    702                 fn1, fn = fn, fn1 + fn
    703 f1 = Fib()
    704 f2 = Fib()
    705 for i in range( 10 ):
    706         print( next( f1 ), next( f2 ) )
    707 
    708 
    709 
    710 
    711 
    712 
    713 \end{python}
    714 \end{lrbox}
    715 
    716 \newbox\myboxB
    717 \begin{lrbox}{\myboxB}
    718 \begin{python}[aboveskip=0pt,belowskip=0pt]
    719 def Fmt():
    720         try:
    721                 while True:
    722                         for g in range( 5 ):
    723                                 for b in range( 4 ):
    724                                         print( `(yield)`, end='' )
    725                                 print( '  ', end='' )
    726                         print()
    727         except GeneratorExit:
    728                 if g != 0 | b != 0:
    729                         print()
    730 fmt = Fmt()
    731 `next( fmt )`                    # prime, next prewritten
    732 for i in range( 41 ):
    733         `fmt.send( 'a' );`      # send to yield
    734 \end{python}
    735 \end{lrbox}
    736 \subfloat[Fibonacci]{\label{f:PythonFibonacci}\usebox\myboxA}
    737 \hspace{3pt}
    738 \vrule
    739 \hspace{3pt}
    740 \subfloat[Formatter]{\label{f:PythonFormatter}\usebox\myboxB}
    741 \caption{Python generator}
    742 \label{f:PythonGenerator}
    743 
    744 \bigskip
    745 
    746947\begin{tabular}{@{}l|l@{}}
    747948\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     
    750951`generator` Driver {
    751952        Status status;
    752         unsigned char byte, * msg; // communication
    753         unsigned int lnth, sum;      // local state
    754         unsigned short int crc;
     953        char byte, * msg; // communication
     954        int lnth, sum;      // local state
     955        short int crc;
    755956};
    756957void ?{}( Driver & d, char * m ) { d.msg = m; }
     
    797998\end{figure}
    798999
    799 Figure~\ref{f:CFAPingPongGen} shows a symmetric generator, where the generator resumes another generator, forming a resume/resume cycle.
     1000Generators can also have symmetric activation using resume/resume to create control-flow cycles among generators.
    8001001(The trivial cycle is a generator resuming itself.)
    8011002This control flow is similar to recursion for functions but without stack growth.
    802 The steps for symmetric control-flow are creating, executing, and terminating the cycle.
     1003Figure~\ref{f:PingPongFullCoroutineSteps} shows the steps for symmetric control-flow using for the ping/pong program in Figure~\ref{f:CFAPingPongGen}.
     1004The program starts by creating the generators, @ping@ and @pong@, and then assigns the partners that form the cycle.
    8031005Constructing the cycle must deal with definition-before-use to close the cycle, \ie, the first generator must know about the last generator, which is not within scope.
    8041006(This issue occurs for any cyclic data structure.)
    805 % The example creates all the generators and then assigns the partners that form the cycle.
    806 % Alternatively, the constructor can assign the partners as they are declared, except the first, and the first-generator partner is set after the last generator declaration to close the cycle.
    807 Once the cycle is formed, the program main resumes one of the generators, and the generators can then traverse an arbitrary cycle using @resume@ to activate partner generator(s).
     1007% (Alternatively, the constructor can assign the partners as they are declared, except the first, and the first-generator partner is set after the last generator declaration to close the cycle.)
     1008Once the cycle is formed, the program main resumes one of the generators, @ping@, and the generators can then traverse an arbitrary number of cycles using @resume@ to activate partner generator(s).
    8081009Terminating the cycle is accomplished by @suspend@ or @return@, both of which go back to the stack frame that started the cycle (program main in the example).
     1010Note, the creator and starter may be different, \eg if the creator calls another function that starts the cycle.
    8091011The starting stack-frame is below the last active generator because the resume/resume cycle does not grow the stack.
    810 Also, since local variables are not retained in the generator function, it does not contain any objects with destructors that must be called, so the  cost is the same as a function return.
    811 Destructor cost occurs when the generator instance is deallocated, which is easily controlled by the programmer.
    812 
    813 Figure~\ref{f:CPingPongSim} shows the implementation of the symmetric generator, where the complexity is the @resume@, which needs an extension to the calling convention to perform a forward rather than backward jump.
    814 This jump-starts at the top of the next generator main to re-execute the normal calling convention to make space on the stack for its local variables.
    815 However, before the jump, the caller must reset its stack (and any registers) equivalent to a @return@, but subsequently jump forward.
    816 This semantics is basically a tail-call optimization, which compilers already perform.
    817 The example shows the assembly code to undo the generator's entry code before the direct jump.
    818 This assembly code depends on what entry code is generated, specifically if there are local variables and the level of optimization.
    819 To provide this new calling convention requires a mechanism built into the compiler, which is beyond the scope of \CFA at this time.
    820 Nevertheless, it is possible to hand generate any symmetric generators for proof of concept and performance testing.
    821 A compiler could also eliminate other artifacts in the generator simulation to further increase performance, \eg LLVM has various coroutine support~\cite{CoroutineTS}, and \CFA can leverage this support should it fork @clang@.
     1012Also, since local variables are not retained in the generator function, there are no objects with destructors to be called, so the cost is the same as a function return.
     1013Destructor cost occurs when the generator instance is deallocated by the creator.
     1014
     1015\begin{figure}
     1016\centering
     1017\input{FullCoroutinePhases.pstex_t}
     1018\vspace*{-10pt}
     1019\caption{Symmetric coroutine steps: Ping / Pong}
     1020\label{f:PingPongFullCoroutineSteps}
     1021\end{figure}
    8221022
    8231023\begin{figure}
     
    8261026\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    8271027`generator PingPong` {
     1028        int N, i;                               // local state
    8281029        const char * name;
    829         int N;
    830         int i;                          // local state
    8311030        PingPong & partner; // rebindable reference
    8321031};
    8331032
    8341033void `main( PingPong & pp )` with(pp) {
     1034
     1035
    8351036        for ( ; i < N; i += 1 ) {
    8361037                sout | name | i;
     
    8501051\begin{cfa}[escapechar={},aboveskip=0pt,belowskip=0pt]
    8511052typedef struct PingPong {
     1053        int restart, N, i;
    8521054        const char * name;
    853         int N, i;
    8541055        struct PingPong * partner;
    855         void * next;
    8561056} PingPong;
    857 #define PPCtor(name, N) {name,N,0,NULL,NULL}
     1057#define PPCtor(name, N) {0, N, 0, name, NULL}
    8581058void comain( PingPong * pp ) {
    859         if ( pp->next ) goto *pp->next;
    860         pp->next = &&cycle;
     1059        static void * states[] = {&&s0, &&s1};
     1060        goto *states[pp->restart];
     1061  s0: pp->restart = 1;
    8611062        for ( ; pp->i < pp->N; pp->i += 1 ) {
    8621063                printf( "%s %d\n", pp->name, pp->i );
    8631064                asm( "mov  %0,%%rdi" : "=m" (pp->partner) );
    8641065                asm( "mov  %rdi,%rax" );
    865                 asm( "popq %rbx" );
     1066                asm( "add  $16, %rsp" );
     1067                asm( "popq %rbp" );
    8661068                asm( "jmp  comain" );
    867           cycle: ;
     1069          s1: ;
    8681070        }
    8691071}
     
    8811083\end{figure}
    8821084
    883 Finally, part of this generator work was inspired by the recent \CCtwenty generator proposal~\cite{C++20Coroutine19} (which they call coroutines).
     1085Figure~\ref{f:CPingPongSim} shows the C implementation of the \CFA symmetric generator, where there is still only one additional field, @restart@, but @resume@ is more complex because it does a forward rather than backward jump.
     1086Before the jump, the parameter for the next call @partner@ is placed into the register used for the first parameter, @rdi@, and the remaining registers are reset for a return.
     1087The @jmp comain@ restarts the function but with a different parameter, so the new call's behavior depends on the state of the coroutine type, \ie branch to restart location with different data state.
     1088While the semantics of call forward is a tail-call optimization, which compilers perform, the generator state is different on each call rather a common state for a tail-recursive function (\ie the parameter to the function never changes during the forward calls).
     1089However, this assembler code depends on what entry code is generated, specifically if there are local variables and the level of optimization.
     1090Hence, internal compiler support is necessary for any forward call or backwards return, \eg LLVM has various coroutine support~\cite{CoroutineTS}, and \CFA can leverage this support should it eventually fork @clang@.
     1091For this reason, \CFA does not support general symmetric generators at this time, but, it is possible to hand generate any symmetric generators, as in Figure~\ref{f:CPingPongSim}, for proof of concept and performance testing.
     1092
     1093Finally, part of this generator work was inspired by the recent \CCtwenty coroutine proposal~\cite{C++20Coroutine19}, which uses the general term coroutine to mean generator.
    8841094Our work provides the same high-performance asymmetric generators as \CCtwenty, and extends their work with symmetric generators.
    8851095An additional \CCtwenty generator feature allows @suspend@ and @resume@ to be followed by a restricted compound statement that is executed after the current generator has reset its stack but before calling the next generator, specified with \CFA syntax:
     
    8961106\label{s:Coroutine}
    8971107
    898 Stackful coroutines extend generator semantics, \ie there is an implicit closure and @suspend@ may appear in a helper function called from the coroutine main.
    899 A coroutine is specified by replacing @generator@ with @coroutine@ for the type.
    900 Coroutine generality results in higher cost for creation, due to dynamic stack allocation, execution, due to context switching among stacks, and terminating, due to possible stack unwinding and dynamic stack deallocation.
     1108Stackful coroutines (Table~\ref{t:ExecutionPropertyComposition} case 5) extend generator semantics with an implicit closure and @suspend@ may appear in a helper function called from the coroutine main because of the separate stack.
     1109Note, simulating coroutines with stacks of generators, \eg Python with @yield from@ cannot handle symmetric control-flow.
     1110Furthermore, all stack components must be of generators, so it is impossible to call a library function passing a generator that yields.
     1111Creating a generator copy of the library function maybe impossible because the library function is opaque.
     1112
     1113A \CFA coroutine is specified by replacing @generator@ with @coroutine@ for the type.
     1114Coroutine generality results in higher cost for creation, due to dynamic stack allocation, for execution, due to context switching among stacks, and for terminating, due to possible stack unwinding and dynamic stack deallocation.
    9011115A series of different kinds of coroutines and their implementations demonstrate how coroutines extend generators.
    9021116
    9031117First, the previous generator examples are converted to their coroutine counterparts, allowing local-state variables to be moved from the generator type into the coroutine main.
    904 \begin{description}
    905 \item[Fibonacci]
    906 Move the declaration of @fn1@ to the start of coroutine main.
     1118Now the coroutine type only contains communication variables between interface functions and the coroutine main.
     1119\begin{center}
     1120\begin{tabular}{@{}l|l|l|l@{}}
     1121\multicolumn{1}{c|}{Fibonacci} & \multicolumn{1}{c|}{Formatter} & \multicolumn{1}{c|}{Device Driver} & \multicolumn{1}{c}{PingPong} \\
     1122\hline
    9071123\begin{cfa}[xleftmargin=0pt]
    908 void main( Fib & fib ) with(fib) {
     1124void main( Fib & fib ) ...
    9091125        `int fn1;`
    910 \end{cfa}
    911 \item[Formatter]
    912 Move the declaration of @g@ and @b@ to the for loops in the coroutine main.
     1126
     1127
     1128\end{cfa}
     1129&
    9131130\begin{cfa}[xleftmargin=0pt]
    9141131for ( `g`; 5 ) {
    9151132        for ( `b`; 4 ) {
    916 \end{cfa}
    917 \item[Device Driver]
    918 Move the declaration of @lnth@ and @sum@ to their points of initialization.
     1133
     1134
     1135\end{cfa}
     1136&
    9191137\begin{cfa}[xleftmargin=0pt]
    920         status = CONT;
    921         `unsigned int lnth = 0, sum = 0;`
    922         ...
    923         `unsigned short int crc = byte << 8;`
    924 \end{cfa}
    925 \item[PingPong]
    926 Move the declaration of @i@ to the for loop in the coroutine main.
     1138status = CONT;
     1139`int lnth = 0, sum = 0;`
     1140...
     1141`short int crc = byte << 8;`
     1142\end{cfa}
     1143&
    9271144\begin{cfa}[xleftmargin=0pt]
    928 void main( PingPong & pp ) with(pp) {
     1145void main( PingPong & pp ) ...
    9291146        for ( `i`; N ) {
    930 \end{cfa}
    931 \end{description}
     1147
     1148
     1149\end{cfa}
     1150\end{tabular}
     1151\end{center}
    9321152It is also possible to refactor code containing local-state and @suspend@ statements into a helper function, like the computation of the CRC for the device driver.
    9331153\begin{cfa}
    934 unsigned int Crc() {
    935         `suspend;`
    936         unsigned short int crc = byte << 8;
    937         `suspend;`
    938         status = (crc | byte) == sum ? MSG : ECRC;
     1154int Crc() {
     1155        `suspend;`  short int crc = byte << 8;
     1156        `suspend;`  status = (crc | byte) == sum ? MSG : ECRC;
    9391157        return crc;
    9401158}
    9411159\end{cfa}
    942 A call to this function is placed at the end of the driver's coroutine-main.
    943 For complex finite-state machines, refactoring is part of normal program abstraction, especially when code is used in multiple places.
     1160A call to this function is placed at the end of the device driver's coroutine-main.
     1161For complex FSMs, refactoring is part of normal program abstraction, especially when code is used in multiple places.
    9441162Again, this complexity is usually associated with execution state rather than data state.
    9451163
    9461164\begin{comment}
    947 Figure~\ref{f:Coroutine3States} creates a @coroutine@ type, @`coroutine` Fib { int fn; }@, which provides communication, @fn@, for the \newterm{coroutine main}, @main@, which runs on the coroutine stack, and possibly multiple interface functions, \eg @next@.
    948 Like the structure in Figure~\ref{f:ExternalState}, the coroutine type allows multiple instances, where instances of this type are passed to the (overloaded) coroutine main.
     1165Figure~\ref{f:Coroutine3States} creates a @coroutine@ type, @`coroutine` Fib { int fn; }@, which provides communication, @fn@, for the \newterm{coroutine main}, @main@, which runs on the coroutine stack, and possibly multiple interface functions, \eg @restart@.
     1166Like the structure in Figure~\ref{f:ExternalState}, the coroutine type allows multiple instances, where instances of this type are passed to the overloaded coroutine main.
    9491167The coroutine main's stack holds the state for the next generation, @f1@ and @f2@, and the code represents the three states in the Fibonacci formula via the three suspend points, to context switch back to the caller's @resume@.
    950 The interface function @next@, takes a Fibonacci instance and context switches to it using @resume@;
     1168The interface function @restart@, takes a Fibonacci instance and context switches to it using @resume@;
    9511169on restart, the Fibonacci field, @fn@, contains the next value in the sequence, which is returned.
    9521170The first @resume@ is special because it allocates the coroutine stack and cocalls its coroutine main on that stack;
     
    11141332\begin{figure}
    11151333\centering
    1116 \lstset{language=CFA,escapechar={},moredelim=**[is][\protect\color{red}]{`}{`}}% allow $
    11171334\begin{tabular}{@{}l@{\hspace{2\parindentlnth}}l@{}}
    11181335\begin{cfa}
    11191336`coroutine` Prod {
    1120         Cons & c;                       // communication
     1337        Cons & c;                       $\C[1.5in]{// communication}$
    11211338        int N, money, receipt;
    11221339};
    11231340void main( Prod & prod ) with( prod ) {
    1124         // 1st resume starts here
    1125         for ( i; N ) {
     1341        for ( i; N ) {          $\C{// 1st resume}\CRT$
    11261342                int p1 = random( 100 ), p2 = random( 100 );
    1127                 sout | p1 | " " | p2;
    11281343                int status = delivery( c, p1, p2 );
    1129                 sout | " $" | money | nl | status;
    11301344                receipt += 1;
    11311345        }
    11321346        stop( c );
    1133         sout | "prod stops";
    11341347}
    11351348int payment( Prod & prod, int money ) {
     
    11521365\begin{cfa}
    11531366`coroutine` Cons {
    1154         Prod & p;                       // communication
     1367        Prod & p;                       $\C[1.5in]{// communication}$
    11551368        int p1, p2, status;
    11561369        bool done;
    11571370};
    11581371void ?{}( Cons & cons, Prod & p ) {
    1159         &cons.p = &p; // reassignable reference
     1372        &cons.p = &p;           $\C{// reassignable reference}$
    11601373        cons.[status, done ] = [0, false];
    11611374}
    11621375void main( Cons & cons ) with( cons ) {
    1163         // 1st resume starts here
    1164         int money = 1, receipt;
     1376        int money = 1, receipt; $\C{// 1st resume}\CRT$
    11651377        for ( ; ! done; ) {
    1166                 sout | p1 | " " | p2 | nl | " $" | money;
    11671378                status += 1;
    11681379                receipt = payment( p, money );
    1169                 sout | " #" | receipt;
    11701380                money += 1;
    11711381        }
    1172         sout | "cons stops";
    11731382}
    11741383int delivery( Cons & cons, int p1, int p2 ) {
     
    11891398
    11901399Figure~\ref{f:ProdCons} shows the ping-pong example in Figure~\ref{f:CFAPingPongGen} extended into a producer/consumer symmetric-coroutine performing bidirectional communication.
    1191 This example is illustrative because both producer/consumer have two interface functions with @resume@s that suspend execution in these interface (helper) functions.
     1400This example is illustrative because both producer and consumer have two interface functions with @resume@s that suspend execution in these interface functions.
    11921401The program main creates the producer coroutine, passes it to the consumer coroutine in its initialization, and closes the cycle at the call to @start@ along with the number of items to be produced.
    1193 The first @resume@ of @prod@ creates @prod@'s stack with a frame for @prod@'s coroutine main at the top, and context switches to it.
    1194 @prod@'s coroutine main starts, creates local-state variables that are retained between coroutine activations, and executes $N$ iterations, each generating two random values, calling the consumer to deliver the values, and printing the status returned from the consumer.
    1195 
    1196 The producer call to @delivery@ transfers values into the consumer's communication variables, resumes the consumer, and returns the consumer status.
    1197 On the first resume, @cons@'s stack is created and initialized, holding local-state variables retained between subsequent activations of the coroutine.
    1198 The consumer iterates until the @done@ flag is set, prints the values delivered by the producer, increments status, and calls back to the producer via @payment@, and on return from @payment@, prints the receipt from the producer and increments @money@ (inflation).
    1199 The call from the consumer to @payment@ introduces the cycle between producer and consumer.
    1200 When @payment@ is called, the consumer copies values into the producer's communication variable and a resume is executed.
    1201 The context switch restarts the producer at the point where it last context switched, so it continues in @delivery@ after the resume.
    1202 @delivery@ returns the status value in @prod@'s coroutine main, where the status is printed.
    1203 The loop then repeats calling @delivery@, where each call resumes the consumer coroutine.
    1204 The context switch to the consumer continues in @payment@.
    1205 The consumer increments and returns the receipt to the call in @cons@'s coroutine main.
    1206 The loop then repeats calling @payment@, where each call resumes the producer coroutine.
     1402The call to @start@ is the first @resume@ of @prod@, which remembers the program main as the starter and creates @prod@'s stack with a frame for @prod@'s coroutine main at the top, and context switches to it.
     1403@prod@'s coroutine main starts, creates local-state variables that are retained between coroutine activations, and executes $N$ iterations, each generating two random values, calling the consumer's @deliver@ function to transfer the values, and printing the status returned from the consumer.
     1404The producer's call to @delivery@ transfers values into the consumer's communication variables, resumes the consumer, and returns the consumer status.
     1405Similarly on the first resume, @cons@'s stack is created and initialized, holding local-state variables retained between subsequent activations of the coroutine.
     1406The symmetric coroutine cycle forms when the consumer calls the producer's @payment@ function, which resumes the producer in the consumer's delivery function.
     1407When the producer calls @delivery@ again, it resumes the consumer in the @payment@ function.
     1408Both interface functions then return to their corresponding coroutine-main functions for the next cycle.
    12071409Figure~\ref{f:ProdConsRuntimeStacks} shows the runtime stacks of the program main, and the coroutine mains for @prod@ and @cons@ during the cycling.
     1410As a consequence of a coroutine retaining its last resumer for suspending back, these reverse pointers allow @suspend@ to cycle \emph{backwards} around a symmetric coroutine cycle.
    12081411
    12091412\begin{figure}
     
    12141417\caption{Producer / consumer runtime stacks}
    12151418\label{f:ProdConsRuntimeStacks}
    1216 
    1217 \medskip
    1218 
    1219 \begin{center}
    1220 \input{FullCoroutinePhases.pstex_t}
    1221 \end{center}
    1222 \vspace*{-10pt}
    1223 \caption{Ping / Pong coroutine steps}
    1224 \label{f:PingPongFullCoroutineSteps}
    12251419\end{figure}
    12261420
    12271421Terminating a coroutine cycle is more complex than a generator cycle, because it requires context switching to the program main's \emph{stack} to shutdown the program, whereas generators started by the program main run on its stack.
    1228 Furthermore, each deallocated coroutine must guarantee all destructors are run for object allocated in the coroutine type \emph{and} allocated on the coroutine's stack at the point of suspension, which can be arbitrarily deep.
    1229 When a coroutine's main ends, its stack is already unwound so any stack allocated objects with destructors have been finalized.
    1230 The na\"{i}ve semantics for coroutine-cycle termination is to context switch to the last resumer, like executing a @suspend@/@return@ in a generator.
     1422Furthermore, each deallocated coroutine must execute all destructors for objects allocated in the coroutine type \emph{and} allocated on the coroutine's stack at the point of suspension, which can be arbitrarily deep.
     1423In the example, termination begins with the producer's loop stopping after N iterations and calling the consumer's @stop@ function, which sets the @done@ flag, resumes the consumer in function @payment@, terminating the call, and the consumer's loop in its coroutine main.
     1424% (Not shown is having @prod@ raise a nonlocal @stop@ exception at @cons@ after it finishes generating values and suspend back to @cons@, which catches the @stop@ exception to terminate its loop.)
     1425When the consumer's main ends, its stack is already unwound so any stack allocated objects with destructors are finalized.
     1426The question now is where does control continue?
     1427
     1428The na\"{i}ve semantics for coroutine-cycle termination is to context switch to the last resumer, like executing a @suspend@ or @return@ in a generator.
    12311429However, for coroutines, the last resumer is \emph{not} implicitly below the current stack frame, as for generators, because each coroutine's stack is independent.
    12321430Unfortunately, it is impossible to determine statically if a coroutine is in a cycle and unrealistic to check dynamically (graph-cycle problem).
    12331431Hence, a compromise solution is necessary that works for asymmetric (acyclic) and symmetric (cyclic) coroutines.
    1234 
    1235 Our solution is to context switch back to the first resumer (starter) once the coroutine ends.
     1432Our solution is to retain a coroutine's starter (first resumer), and context switch back to the starter when the coroutine ends.
     1433Hence, the consumer restarts its first resumer, @prod@, in @stop@, and when the producer ends, it restarts its first resumer, program main, in @start@ (see dashed lines from the end of the coroutine mains in Figure~\ref{f:ProdConsRuntimeStacks}).
    12361434This semantics works well for the most common asymmetric and symmetric coroutine usage patterns.
    1237 For asymmetric coroutines, it is common for the first resumer (starter) coroutine to be the only resumer.
    1238 All previous generators converted to coroutines have this property.
    1239 For symmetric coroutines, it is common for the cycle creator to persist for the lifetime of the cycle.
    1240 Hence, the starter coroutine is remembered on the first resume and ending the coroutine resumes the starter.
    1241 Figure~\ref{f:ProdConsRuntimeStacks} shows this semantic by the dashed lines from the end of the coroutine mains: @prod@ starts @cons@ so @cons@ resumes @prod@ at the end, and the program main starts @prod@ so @prod@ resumes the program main at the end.
    1242 For other scenarios, it is always possible to devise a solution with additional programming effort, such as forcing the cycle forward (backward) to a safe point before starting termination.
    1243 
    1244 The producer/consumer example does not illustrate the full power of the starter semantics because @cons@ always ends first.
    1245 Assume generator @PingPong@ is converted to a coroutine.
    1246 Figure~\ref{f:PingPongFullCoroutineSteps} shows the creation, starter, and cyclic execution steps of the coroutine version.
    1247 The program main creates (declares) coroutine instances @ping@ and @pong@.
    1248 Next, program main resumes @ping@, making it @ping@'s starter, and @ping@'s main resumes @pong@'s main, making it @pong@'s starter.
    1249 Execution forms a cycle when @pong@ resumes @ping@, and cycles $N$ times.
    1250 By adjusting $N$ for either @ping@/@pong@, it is possible to have either one finish first, instead of @pong@ always ending first.
    1251 If @pong@ ends first, it resumes its starter @ping@ in its coroutine main, then @ping@ ends and resumes its starter the program main in function @start@.
    1252 If @ping@ ends first, it resumes its starter the program main in function @start@.
    1253 Regardless of the cycle complexity, the starter stack always leads back to the program main, but the stack can be entered at an arbitrary point.
    1254 Once back at the program main, coroutines @ping@ and @pong@ are deallocated.
    1255 For generators, deallocation runs the destructors for all objects in the generator type.
    1256 For coroutines, deallocation deals with objects in the coroutine type and must also run the destructors for any objects pending on the coroutine's stack for any unterminated coroutine.
    1257 Hence, if a coroutine's destructor detects the coroutine is not ended, it implicitly raises a cancellation exception (uncatchable exception) at the coroutine and resumes it so the cancellation exception can propagate to the root of the coroutine's stack destroying all local variable on the stack.
    1258 So the \CFA semantics for the generator and coroutine, ensure both can be safely deallocated at any time, regardless of their current state, like any other aggregate object.
    1259 Explicitly raising normal exceptions at another coroutine can replace flag variables, like @stop@, \eg @prod@ raises a @stop@ exception at @cons@ after it finishes generating values and resumes @cons@, which catches the @stop@ exception to terminate its loop.
    1260 
    1261 Finally, there is an interesting effect for @suspend@ with symmetric coroutines.
    1262 A coroutine must retain its last resumer to suspend back because the resumer is on a different stack.
    1263 These reverse pointers allow @suspend@ to cycle \emph{backwards}, which may be useful in certain cases.
    1264 However, there is an anomaly if a coroutine resumes itself, because it overwrites its last resumer with itself, losing the ability to resume the last external resumer.
    1265 To prevent losing this information, a self-resume does not overwrite the last resumer.
    1266 
    1267 
    1268 \subsection{Generator / Coroutine Implementation}
    1269 
    1270 A significant implementation challenge for generators/coroutines (and threads in Section~\ref{s:threads}) is adding extra fields to the custom types and related functions, \eg inserting code after/before the coroutine constructor/destructor and @main@ to create/initialize/de-initialize/destroy any extra fields, \eg stack.
    1271 There are several solutions to these problem, which follow from the object-oriented flavour of adopting custom types.
     1435For asymmetric coroutines, it is common for the first resumer (starter) coroutine to be the only resumer;
     1436for symmetric coroutines, it is common for the cycle creator to persist for the lifetime of the cycle.
     1437For other scenarios, it is always possible to devise a solution with additional programming effort, such as forcing the cycle forward or backward to a safe point before starting termination.
     1438
     1439Note, the producer/consumer example does not illustrate the full power of the starter semantics because @cons@ always ends first.
     1440Assume generator @PingPong@ in Figure~\ref{f:PingPongSymmetricGenerator} is converted to a coroutine.
     1441Unlike generators, coroutines have a starter structure with multiple levels, where the program main starts @ping@ and @ping@ starts @pong@.
     1442By adjusting $N$ for either @ping@ or @pong@, it is possible to have either finish first.
     1443If @pong@ ends first, it resumes its starter @ping@ in its coroutine main, then @ping@ ends and resumes its starter the program main on return;
     1444if @ping@ ends first, it resumes its starter the program main on return.
     1445Regardless of the cycle complexity, the starter structure always leads back to the program main, but the path can be entered at an arbitrary point.
     1446Once back at the program main (creator), coroutines @ping@ and @pong@ are deallocated, running any destructors for objects within the coroutine and possibly deallocating any coroutine stacks for non-terminated coroutines, where stack deallocation implies stack unwinding to find destructors for allocated objects on the stack.
     1447Hence, the \CFA termination semantics for the generator and coroutine ensure correct deallocation semantics, regardless of the coroutine's state (terminated or active), like any other aggregate object.
     1448
     1449
     1450\subsection{Generator / coroutine implementation}
     1451
     1452A significant implementation challenge for generators and coroutines (and threads in Section~\ref{s:threads}) is adding extra fields to the custom types and related functions, \eg inserting code after/before the coroutine constructor/destructor and @main@ to create/initialize/de-initialize/destroy any extra fields, \eg the coroutine stack.
     1453There are several solutions to this problem, which follow from the object-oriented flavor of adopting custom types.
    12721454
    12731455For object-oriented languages, inheritance is used to provide extra fields and code via explicit inheritance:
     
    12761458\end{cfa}
    12771459% The problem is that the programming language and its tool chain, \eg debugger, @valgrind@, need to understand @baseCoroutine@ because it infers special property, so type @baseCoroutine@ becomes a de facto keyword and all types inheriting from it are implicitly custom types.
    1278 The problem is that some special properties are not handled by existing language semantics, \eg the execution of constructors/destructors is in the wrong order to implicitly start threads because the thread must start \emph{after} all constructors as it relies on a completely initialized object, but the inherited constructor runs \emph{before} the derived.
     1460The problem is that some special properties are not handled by existing language semantics, \eg the execution of constructors and destructors is in the wrong order to implicitly start threads because the thread must start \emph{after} all constructors as it relies on a completely initialized object, but the inherited constructor runs \emph{before} the derived.
    12791461Alternatives, such as explicitly starting threads as in Java, are repetitive and forgetting to call start is a common source of errors.
    12801462An alternative is composition:
     
    12941476Users wanting to extend custom types or build their own can only do so in ways offered by the language.
    12951477Furthermore, implementing custom types without language support may display the power of a programming language.
    1296 \CFA blends the two approaches, providing custom type for idiomatic \CFA code, while extending and building new custom types is still possible, similar to Java concurrency with builtin and library.
     1478\CFA blends the two approaches, providing custom type for idiomatic \CFA code, while extending and building new custom types is still possible, similar to Java concurrency with builtin and library (@java.util.concurrent@) monitors.
    12971479
    12981480Part of the mechanism to generalize custom types is the \CFA trait~\cite[\S~2.3]{Moss18}, \eg the definition for custom-type @coroutine@ is anything satisfying the trait @is_coroutine@, and this trait both enforces and restricts the coroutine-interface functions.
     
    13041486forall( `dtype` T | is_coroutine(T) ) void $suspend$( T & ), resume( T & );
    13051487\end{cfa}
    1306 Note, copying generators/coroutines/threads is not meaningful.
    1307 For example, both the resumer and suspender descriptors can have bidirectional pointers;
    1308 copying these coroutines does not update the internal pointers so behaviour of both copies would be difficult to understand.
    1309 Furthermore, two coroutines cannot logically execute on the same stack.
    1310 A deep coroutine copy, which copies the stack, is also meaningless in an unmanaged language (no garbage collection), like C, because the stack may contain pointers to object within it that require updating for the copy.
    1311 The \CFA @dtype@ property provides no \emph{implicit} copying operations and the @is_coroutine@ trait provides no \emph{explicit} copying operations, so all coroutines must be passed by reference (pointer).
    1312 The function definitions ensure there is a statically typed @main@ function that is the starting point (first stack frame) of a coroutine, and a mechanism to get (read) the coroutine descriptor from its handle.
    1313 The @main@ function has no return value or additional parameters because the coroutine type allows an arbitrary number of interface functions with corresponding arbitrary typed input/output values versus fixed ones.
     1488Note, copying generators, coroutines, and threads is undefined because multiple objects cannot execute on a shared stack and stack copying does not work in unmanaged languages (no garbage collection), like C, because the stack may contain pointers to objects within it that require updating for the copy.
     1489The \CFA @dtype@ property provides no \emph{implicit} copying operations and the @is_coroutine@ trait provides no \emph{explicit} copying operations, so all coroutines must be passed by reference or pointer.
     1490The function definitions ensure there is a statically typed @main@ function that is the starting point (first stack frame) of a coroutine, and a mechanism to read the coroutine descriptor from its handle.
     1491The @main@ function has no return value or additional parameters because the coroutine type allows an arbitrary number of interface functions with arbitrary typed input and output values versus fixed ones.
    13141492The advantage of this approach is that users can easily create different types of coroutines, \eg changing the memory layout of a coroutine is trivial when implementing the @get_coroutine@ function, and possibly redefining \textsf{suspend} and @resume@.
    13151493
     
    13521530The combination of custom types and fundamental @trait@ description of these types allows a concise specification for programmers and tools, while more advanced programmers can have tighter control over memory layout and initialization.
    13531531
    1354 Figure~\ref{f:CoroutineMemoryLayout} shows different memory-layout options for a coroutine (where a task is similar).
    1355 The coroutine handle is the @coroutine@ instance containing programmer specified type global/communication variables across interface functions.
     1532Figure~\ref{f:CoroutineMemoryLayout} shows different memory-layout options for a coroutine (where a thread is similar).
     1533The coroutine handle is the @coroutine@ instance containing programmer specified type global and communication variables across interface functions.
    13561534The coroutine descriptor contains all implicit declarations needed by the runtime, \eg @suspend@/@resume@, and can be part of the coroutine handle or separate.
    13571535The coroutine stack can appear in a number of locations and be fixed or variable sized.
    1358 Hence, the coroutine's stack could be a VLS\footnote{
    1359 We are examining variable-sized structures (VLS), where fields can be variable-sized structures or arrays.
    1360 Once allocated, a VLS is fixed sized.}
     1536Hence, the coroutine's stack could be a variable-length structure (VLS)
     1537% \footnote{
     1538% We are examining VLSs, where fields can be variable-sized structures or arrays.
     1539% Once allocated, a VLS is fixed sized.}
    13611540on the allocating stack, provided the allocating stack is large enough.
    1362 For a VLS stack allocation/deallocation is an inexpensive adjustment of the stack pointer, modulo any stack constructor costs (\eg initial frame setup).
    1363 For heap stack allocation, allocation/deallocation is an expensive heap allocation (where the heap can be a shared resource), modulo any stack constructor costs.
    1364 With heap stack allocation, it is also possible to use a split (segmented) stack calling convention, available with gcc and clang, so the stack is variable sized.
    1365 Currently, \CFA supports stack/heap allocated descriptors but only fixed-sized heap allocated stacks.
     1541For a VLS stack allocation and deallocation is an inexpensive adjustment of the stack pointer, modulo any stack constructor costs to initial frame setup.
     1542For stack allocation in the heap, allocation and deallocation is an expensive allocation, where the heap can be a shared resource, modulo any stack constructor costs.
     1543It is also possible to use a split or segmented stack calling convention, available with gcc and clang, allowing a variable-sized stack via a set of connected blocks in the heap.
     1544Currently, \CFA supports stack and heap allocated descriptors but only fixed-sized heap allocated stacks.
    13661545In \CFA debug-mode, the fixed-sized stack is terminated with a write-only page, which catches most stack overflows.
    13671546Experience teaching concurrency with \uC~\cite{CS343} shows fixed-sized stacks are rarely an issue for students.
    1368 Split-stack allocation is under development but requires recompilation of legacy code, which may be impossible.
     1547Split-stack allocation is under development but requires recompilation of legacy code, which is not always possible.
    13691548
    13701549\begin{figure}
     
    13801559
    13811560Concurrency is nondeterministic scheduling of independent sequential execution paths (threads), where each thread has its own stack.
    1382 A single thread with multiple call stacks, \newterm{coroutining}~\cite{Conway63,Marlin80}, does \emph{not} imply concurrency~\cite[\S~2]{Buhr05a}.
    1383 In coroutining, coroutines self-schedule the thread across stacks so execution is deterministic.
     1561A single thread with multiple stacks, \ie coroutining, does \emph{not} imply concurrency~\cite[\S~3]{Buhr05a}.
     1562Coroutining self-schedule the thread across stacks so execution is deterministic.
    13841563(It is \emph{impossible} to generate a concurrency error when coroutining.)
    1385 However, coroutines are a stepping stone towards concurrency.
    1386 
    1387 The transition to concurrency, even for a single thread with multiple stacks, occurs when coroutines context switch to a \newterm{scheduling coroutine}, introducing non-determinism from the coroutine perspective~\cite[\S~3,]{Buhr05a}.
     1564
     1565The transition to concurrency, even for a single thread with multiple stacks, occurs when coroutines context switch to a \newterm{scheduling coroutine}, introducing non-determinism from the coroutine perspective~\cite[\S~3]{Buhr05a}.
    13881566Therefore, a minimal concurrency system requires coroutines \emph{in conjunction with a nondeterministic scheduler}.
    1389 The resulting execution system now follows a cooperative threading model~\cite{Adya02,libdill}, called \newterm{non-preemptive scheduling}.
    1390 Adding \newterm{preemption} introduces non-cooperative scheduling, where context switching occurs randomly between any two instructions often based on a timer interrupt, called \newterm{preemptive scheduling}.
    1391 While a scheduler introduces uncertain execution among explicit context switches, preemption introduces uncertainty by introducing implicit context switches.
     1567The resulting execution system now follows a cooperative threading-model~\cite{Adya02,libdill} because context-switching points to the scheduler are known, but the next unblocking point is unknown due to the scheduler.
     1568Adding \newterm{preemption} introduces \newterm{non-cooperative} or \newterm{preemptive} scheduling, where context switching points to the scheduler are unknown as they can occur randomly between any two instructions often based on a timer interrupt.
    13921569Uncertainty gives the illusion of parallelism on a single processor and provides a mechanism to access and increase performance on multiple processors.
    1393 The reason is that the scheduler/runtime have complete knowledge about resources and how to best utilized them.
    1394 However, the introduction of unrestricted nondeterminism results in the need for \newterm{mutual exclusion} and \newterm{synchronization}, which restrict nondeterminism for correctness;
     1570The reason is that the scheduler and runtime have complete knowledge about resources and how to best utilized them.
     1571However, the introduction of unrestricted nondeterminism results in the need for \newterm{mutual exclusion} and \newterm{synchronization}~\cite[\S~4]{Buhr05a}, which restrict nondeterminism for correctness;
    13951572otherwise, it is impossible to write meaningful concurrent programs.
    13961573Optimal concurrent performance is often obtained by having as much nondeterminism as mutual exclusion and synchronization correctness allow.
    13971574
    1398 A scheduler can either be a stackless or stackful.
     1575A scheduler can also be stackless or stackful.
    13991576For stackless, the scheduler performs scheduling on the stack of the current coroutine and switches directly to the next coroutine, so there is one context switch.
    14001577For stackful, the current coroutine switches to the scheduler, which performs scheduling, and it then switches to the next coroutine, so there are two context switches.
     
    14051582\label{s:threads}
    14061583
    1407 Threading needs the ability to start a thread and wait for its completion.
    1408 A common API for this ability is @fork@ and @join@.
    1409 \begin{cquote}
    1410 \begin{tabular}{@{}lll@{}}
    1411 \multicolumn{1}{c}{\textbf{Java}} & \multicolumn{1}{c}{\textbf{\Celeven}} & \multicolumn{1}{c}{\textbf{pthreads}} \\
    1412 \begin{cfa}
    1413 class MyTask extends Thread {...}
    1414 mytask t = new MyTask(...);
     1584Threading (Table~\ref{t:ExecutionPropertyComposition} case 11) needs the ability to start a thread and wait for its completion, where a common API is @fork@ and @join@.
     1585\vspace{4pt}
     1586\par\noindent
     1587\begin{tabular}{@{}l|l|l@{}}
     1588\multicolumn{1}{c|}{\textbf{Java}} & \multicolumn{1}{c|}{\textbf{\Celeven}} & \multicolumn{1}{c}{\textbf{pthreads}} \\
     1589\hline
     1590\begin{cfa}
     1591class MyThread extends Thread {...}
     1592mythread t = new MyThread(...);
    14151593`t.start();` // start
    14161594// concurrency
     
    14191597&
    14201598\begin{cfa}
    1421 class MyTask { ... } // functor
    1422 MyTask mytask;
    1423 `thread t( mytask, ... );` // start
     1599class MyThread { ... } // functor
     1600MyThread mythread;
     1601`thread t( mythread, ... );` // start
    14241602// concurrency
    14251603`t.join();` // wait
     
    14341612\end{cfa}
    14351613\end{tabular}
    1436 \end{cquote}
    1437 \CFA has a simpler approach using a custom @thread@ type and leveraging declaration semantics (allocation/deallocation), where threads implicitly @fork@ after construction and @join@ before destruction.
    1438 \begin{cfa}
    1439 thread MyTask {};
    1440 void main( MyTask & this ) { ... }
     1614\vspace{1pt}
     1615\par\noindent
     1616\CFA has a simpler approach using a custom @thread@ type and leveraging declaration semantics, allocation and deallocation, where threads implicitly @fork@ after construction and @join@ before destruction.
     1617\begin{cfa}
     1618thread MyThread {};
     1619void main( MyThread & this ) { ... }
    14411620int main() {
    1442         MyTask team`[10]`; $\C[2.5in]{// allocate stack-based threads, implicit start after construction}$
     1621        MyThread team`[10]`; $\C[2.5in]{// allocate stack-based threads, implicit start after construction}$
    14431622        // concurrency
    14441623} $\C{// deallocate stack-based threads, implicit joins before destruction}$
    14451624\end{cfa}
    1446 This semantic ensures a thread is started and stopped exactly once, eliminating some programming error, and scales to multiple threads for basic (termination) synchronization.
    1447 For block allocation to arbitrary depth, including recursion, threads are created/destroyed in a lattice structure (tree with top and bottom).
     1625This semantic ensures a thread is started and stopped exactly once, eliminating some programming error, and scales to multiple threads for basic termination synchronization.
     1626For block allocation to arbitrary depth, including recursion, threads are created and destroyed in a lattice structure (tree with top and bottom).
    14481627Arbitrary topologies are possible using dynamic allocation, allowing threads to outlive their declaration scope, identical to normal dynamic allocation.
    14491628\begin{cfa}
    1450 MyTask * factory( int N ) { ... return `anew( N )`; } $\C{// allocate heap-based threads, implicit start after construction}$
     1629MyThread * factory( int N ) { ... return `anew( N )`; } $\C{// allocate heap-based threads, implicit start after construction}$
    14511630int main() {
    1452         MyTask * team = factory( 10 );
     1631        MyThread * team = factory( 10 );
    14531632        // concurrency
    1454         `delete( team );` $\C{// deallocate heap-based threads, implicit joins before destruction}\CRT$
     1633        `adelete( team );` $\C{// deallocate heap-based threads, implicit joins before destruction}\CRT$
    14551634}
    14561635\end{cfa}
     
    14931672
    14941673
    1495 \subsection{Thread Implementation}
     1674\subsection{Thread implementation}
    14961675
    14971676Threads in \CFA are user level run by runtime kernel threads (see Section~\ref{s:CFARuntimeStructure}), where user threads provide concurrency and kernel threads provide parallelism.
    1498 Like coroutines, and for the same design reasons, \CFA provides a custom @thread@ type and a @trait@ to enforce and restrict the task-interface functions.
     1677Like coroutines, and for the same design reasons, \CFA provides a custom @thread@ type and a @trait@ to enforce and restrict the thread-interface functions.
    14991678\begin{cquote}
    15001679\begin{tabular}{@{}c@{\hspace{3\parindentlnth}}c@{}}
     
    15161695\end{tabular}
    15171696\end{cquote}
    1518 Like coroutines, the @dtype@ property prevents \emph{implicit} copy operations and the @is_thread@ trait provides no \emph{explicit} copy operations, so threads must be passed by reference (pointer).
    1519 Similarly, the function definitions ensure there is a statically typed @main@ function that is the thread starting point (first stack frame), a mechanism to get (read) the thread descriptor from its handle, and a special destructor to prevent deallocation while the thread is executing.
     1697Like coroutines, the @dtype@ property prevents \emph{implicit} copy operations and the @is_thread@ trait provides no \emph{explicit} copy operations, so threads must be passed by reference or pointer.
     1698Similarly, the function definitions ensure there is a statically typed @main@ function that is the thread starting point (first stack frame), a mechanism to read the thread descriptor from its handle, and a special destructor to prevent deallocation while the thread is executing.
    15201699(The qualifier @mutex@ for the destructor parameter is discussed in Section~\ref{s:Monitor}.)
    15211700The difference between the coroutine and thread is that a coroutine borrows a thread from its caller, so the first thread resuming a coroutine creates the coroutine's stack and starts running the coroutine main on the stack;
    15221701whereas, a thread is scheduling for execution in @main@ immediately after its constructor is run.
    1523 No return value or additional parameters are necessary for this function because the @thread@ type allows an arbitrary number of interface functions with corresponding arbitrary typed input/output values.
     1702No return value or additional parameters are necessary for this function because the @thread@ type allows an arbitrary number of interface functions with corresponding arbitrary typed input and output values.
    15241703
    15251704
     
    15271706\label{s:MutualExclusionSynchronization}
    15281707
    1529 Unrestricted nondeterminism is meaningless as there is no way to know when the result is completed without synchronization.
     1708Unrestricted nondeterminism is meaningless as there is no way to know when a result is completed and safe to access.
    15301709To produce meaningful execution requires clawing back some determinism using mutual exclusion and synchronization, where mutual exclusion provides access control for threads using shared data, and synchronization is a timing relationship among threads~\cite[\S~4]{Buhr05a}.
    1531 Some concurrent systems eliminate mutable shared-state by switching to stateless communication like message passing~\cite{Thoth,Harmony,V-Kernel,MPI} (Erlang, MPI), channels~\cite{CSP} (CSP,Go), actors~\cite{Akka} (Akka, Scala), or functional techniques (Haskell).
     1710The shared data protected by mutual exclusion is called a \newterm{critical section}~\cite{Dijkstra65}, and the protection can be simple, only 1 thread, or complex, only N kinds of threads, \eg group~\cite{Joung00} or readers/writer~\cite{Courtois71} problems.
     1711Without synchronization control in a critical section, an arriving thread can barge ahead of preexisting waiter threads resulting in short/long-term starvation, staleness and freshness problems, and incorrect transfer of data.
     1712Preventing or detecting barging is a challenge with low-level locks, but made easier through higher-level constructs.
     1713This challenge is often split into two different approaches: barging \emph{avoidance} and \emph{prevention}.
     1714Approaches that unconditionally releasing a lock for competing threads to acquire must use barging avoidance with flag/counter variable(s) to force barging threads to wait;
     1715approaches that conditionally hold locks during synchronization, \eg baton-passing~\cite{Andrews89}, prevent barging completely.
     1716
     1717At the lowest level, concurrent control is provided by atomic operations, upon which different kinds of locking mechanisms are constructed, \eg spin locks, semaphores~\cite{Dijkstra68b}, barriers, and path expressions~\cite{Campbell74}.
     1718However, for productivity it is always desirable to use the highest-level construct that provides the necessary efficiency~\cite{Hochstein05}.
     1719A significant challenge with locks is composability because it takes careful organization for multiple locks to be used while preventing deadlock.
     1720Easing composability is another feature higher-level mutual-exclusion mechanisms can offer.
     1721Some concurrent systems eliminate mutable shared-state by switching to non-shared communication like message passing~\cite{Thoth,Harmony,V-Kernel,MPI} (Erlang, MPI), channels~\cite{CSP} (CSP,Go), actors~\cite{Akka} (Akka, Scala), or functional techniques (Haskell).
    15321722However, these approaches introduce a new communication mechanism for concurrency different from the standard communication using function call/return.
    1533 Hence, a programmer must learn and manipulate two sets of design/programming patterns.
     1723Hence, a programmer must learn and manipulate two sets of design and programming patterns.
    15341724While this distinction can be hidden away in library code, effective use of the library still has to take both paradigms into account.
    1535 In contrast, approaches based on stateful models more closely resemble the standard call/return programming model, resulting in a single programming paradigm.
    1536 
    1537 At the lowest level, concurrent control is implemented by atomic operations, upon which different kinds of locking mechanisms are constructed, \eg semaphores~\cite{Dijkstra68b}, barriers, and path expressions~\cite{Campbell74}.
    1538 However, for productivity it is always desirable to use the highest-level construct that provides the necessary efficiency~\cite{Hochstein05}.
    1539 A newer approach for restricting non-determinism is transactional memory~\cite{Herlihy93}.
    1540 While this approach is pursued in hardware~\cite{Nakaike15} and system languages, like \CC~\cite{Cpp-Transactions}, the performance and feature set is still too restrictive to be the main concurrency paradigm for system languages, which is why it is rejected as the core paradigm for concurrency in \CFA.
    1541 
    1542 One of the most natural, elegant, and efficient mechanisms for mutual exclusion and synchronization for shared-memory systems is the \emph{monitor}.
    1543 First proposed by Brinch Hansen~\cite{Hansen73} and later described and extended by C.A.R.~Hoare~\cite{Hoare74}, many concurrent programming languages provide monitors as an explicit language construct: \eg Concurrent Pascal~\cite{ConcurrentPascal}, Mesa~\cite{Mesa}, Modula~\cite{Modula-2}, Turing~\cite{Turing:old}, Modula-3~\cite{Modula-3}, NeWS~\cite{NeWS}, Emerald~\cite{Emerald}, \uC~\cite{Buhr92a} and Java~\cite{Java}.
    1544 In addition, operating-system kernels and device drivers have a monitor-like structure, although they often use lower-level primitives such as mutex locks or semaphores to simulate monitors.
    1545 For these reasons, \CFA selected monitors as the core high-level concurrency construct, upon which higher-level approaches can be easily constructed.
    1546 
    1547 
    1548 \subsection{Mutual Exclusion}
    1549 
    1550 A group of instructions manipulating a specific instance of shared data that must be performed atomically is called a \newterm{critical section}~\cite{Dijkstra65}, which is enforced by \newterm{simple mutual-exclusion}.
    1551 The generalization is called a \newterm{group critical-section}~\cite{Joung00}, where multiple tasks with the same session use the resource simultaneously and different sessions are segregated, which is enforced by \newterm{complex mutual-exclusion} providing the correct kind and number of threads using a group critical-section.
    1552 The readers/writer problem~\cite{Courtois71} is an instance of a group critical-section, where readers share a session but writers have a unique session.
    1553 
    1554 However, many solutions exist for mutual exclusion, which vary in terms of performance, flexibility and ease of use.
    1555 Methods range from low-level locks, which are fast and flexible but require significant attention for correctness, to higher-level concurrency techniques, which sacrifice some performance to improve ease of use.
    1556 Ease of use comes by either guaranteeing some problems cannot occur, \eg deadlock free, or by offering a more explicit coupling between shared data and critical section.
    1557 For example, the \CC @std::atomic<T>@ offers an easy way to express mutual-exclusion on a restricted set of operations, \eg reading/writing, for numerical types.
    1558 However, a significant challenge with locks is composability because it takes careful organization for multiple locks to be used while preventing deadlock.
    1559 Easing composability is another feature higher-level mutual-exclusion mechanisms can offer.
    1560 
    1561 
    1562 \subsection{Synchronization}
    1563 
    1564 Synchronization enforces relative ordering of execution, and synchronization tools provide numerous mechanisms to establish these timing relationships.
    1565 Low-level synchronization primitives offer good performance and flexibility at the cost of ease of use;
    1566 higher-level mechanisms often simplify usage by adding better coupling between synchronization and data, \eg receive-specific versus receive-any thread in message passing or offering specialized solutions, \eg barrier lock.
    1567 Often synchronization is used to order access to a critical section, \eg ensuring a waiting writer thread enters the critical section before a calling reader thread.
    1568 If the calling reader is scheduled before the waiting writer, the reader has barged.
    1569 Barging can result in staleness/freshness problems, where a reader barges ahead of a writer and reads temporally stale data, or a writer barges ahead of another writer overwriting data with a fresh value preventing the previous value from ever being read (lost computation).
    1570 Preventing or detecting barging is an involved challenge with low-level locks, which is made easier through higher-level constructs.
    1571 This challenge is often split into two different approaches: barging avoidance and prevention.
    1572 Algorithms that unconditionally releasing a lock for competing threads to acquire use barging avoidance during synchronization to force a barging thread to wait;
    1573 algorithms that conditionally hold locks during synchronization, \eg baton-passing~\cite{Andrews89}, prevent barging completely.
     1725In contrast, approaches based on shared-state models more closely resemble the standard call and return programming model, resulting in a single programming paradigm.
     1726Finally, a newer approach for restricting non-determinism is transactional memory~\cite{Herlihy93}.
     1727While this approach is pursued in hardware~\cite{Nakaike15} and system languages, like \CC~\cite{Cpp-Transactions}, the performance and feature set is still too restrictive~\cite{Cascaval08,Boehm09} to be the main concurrency paradigm for system languages.
    15741728
    15751729
     
    15771731\label{s:Monitor}
    15781732
    1579 A \textbf{monitor} is a set of functions that ensure mutual exclusion when accessing shared state.
    1580 More precisely, a monitor is a programming technique that implicitly binds mutual exclusion to static function scope, as opposed to locks, where mutual-exclusion is defined by acquire/release calls, independent of lexical context (analogous to block and heap storage allocation).
    1581 Restricting acquire/release points eases programming, comprehension, and maintenance, at a slight cost in flexibility and efficiency.
    1582 \CFA uses a custom @monitor@ type and leverages declaration semantics (deallocation) to protect active or waiting threads in a monitor.
    1583 
    1584 The following is a \CFA monitor implementation of an atomic counter.
    1585 \begin{cfa}[morekeywords=nomutex]
    1586 `monitor` Aint { int cnt; }; $\C[4.25in]{// atomic integer counter}$
    1587 int ++?( Aint & `mutex`$\(_{opt}\)$ this ) with( this ) { return ++cnt; } $\C{// increment}$
    1588 int ?=?( Aint & `mutex`$\(_{opt}\)$ lhs, int rhs ) with( lhs ) { cnt = rhs; } $\C{// conversions with int}\CRT$
    1589 int ?=?( int & lhs, Aint & `mutex`$\(_{opt}\)$ rhs ) with( rhs ) { lhs = cnt; }
    1590 \end{cfa}
    1591 % The @Aint@ constructor, @?{}@, uses the \lstinline[morekeywords=nomutex]@nomutex@ qualifier indicating mutual exclusion is unnecessary during construction because an object is inaccessible (private) until after it is initialized.
    1592 % (While a constructor may publish its address into a global variable, doing so generates a race-condition.)
    1593 The prefix increment operation, @++?@, is normally @mutex@, indicating mutual exclusion is necessary during function execution, to protect the incrementing from race conditions, unless there is an atomic increment instruction for the implementation type.
    1594 The assignment operators provide bidirectional conversion between an atomic and normal integer without accessing field @cnt@;
    1595 these operations only need @mutex@, if reading/writing the implementation type is not atomic.
    1596 The atomic counter is used without any explicit mutual-exclusion and provides thread-safe semantics, which is similar to the \CC template @std::atomic@.
    1597 \begin{cfa}
     1733One of the most natural, elegant, efficient, high-level mechanisms for mutual exclusion and synchronization for shared-memory systems is the \emph{monitor} (Table~\ref{t:ExecutionPropertyComposition} case 2).
     1734First proposed by Brinch Hansen~\cite{Hansen73} and later described and extended by C.A.R.~Hoare~\cite{Hoare74}, many concurrent programming languages provide monitors as an explicit language construct: \eg Concurrent Pascal~\cite{ConcurrentPascal}, Mesa~\cite{Mesa}, Modula~\cite{Modula-2}, Turing~\cite{Turing:old}, Modula-3~\cite{Modula-3}, NeWS~\cite{NeWS}, Emerald~\cite{Emerald}, \uC~\cite{Buhr92a} and Java~\cite{Java}.
     1735In addition, operating-system kernels and device drivers have a monitor-like structure, although they often use lower-level primitives such as mutex locks or semaphores to manually implement a monitor.
     1736For these reasons, \CFA selected monitors as the core high-level concurrency construct, upon which higher-level approaches can be easily constructed.
     1737
     1738Figure~\ref{f:AtomicCounter} compares a \CFA and Java monitor implementing an atomic counter.
     1739(Like other concurrent programming languages, \CFA and Java have performant specializations for the basic types using atomic instructions.)
     1740A \newterm{monitor} is a set of functions that ensure mutual exclusion when accessing shared state.
     1741(Note, in \CFA, @monitor@ is short-hand for @mutex struct@.)
     1742More precisely, a monitor is a programming technique that implicitly binds mutual exclusion to static function scope by call and return, as opposed to locks, where mutual exclusion is defined by acquire/release calls, independent of lexical context (analogous to block and heap storage allocation).
     1743Restricting acquire and release points eases programming, comprehension, and maintenance, at a slight cost in flexibility and efficiency.
     1744As for other special types, \CFA has a custom @monitor@ type.
     1745
     1746\begin{figure}
     1747\centering
     1748
     1749\begin{lrbox}{\myboxA}
     1750\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     1751`monitor` Aint { // atomic integer counter
     1752        int cnt;
     1753};
     1754int ++?( Aint & `mutex` this ) with(this) { return ++cnt; }
     1755int ?=?( Aint & `mutex` lhs, int rhs ) with(lhs) { cnt = rhs; }
     1756int ?=?(int & lhs, Aint & rhs) with(rhs) { lhs = cnt; }
     1757
    15981758int i = 0, j = 0, k = 5;
    1599 Aint x = { 0 }, y = { 0 }, z = { 5 }; $\C{// no mutex required}$
    1600 ++x; ++y; ++z; $\C{// safe increment by multiple threads}$
    1601 x = 2; y = i; z = k; $\C{// conversions}$
    1602 i = x; j = y; k = z;
    1603 \end{cfa}
    1604 
    1605 \CFA monitors have \newterm{multi-acquire} semantics so the thread in the monitor may acquire it multiple times without deadlock, allowing recursion and calling other interface functions.
    1606 \begin{cfa}
    1607 monitor M { ... } m;
    1608 void foo( M & mutex m ) { ... } $\C{// acquire mutual exclusion}$
    1609 void bar( M & mutex m ) { $\C{// acquire mutual exclusion}$
    1610         ... `bar( m );` ... `foo( m );` ... $\C{// reacquire mutual exclusion}$
    1611 }
    1612 \end{cfa}
    1613 \CFA monitors also ensure the monitor lock is released regardless of how an acquiring function ends (normal or exceptional), and returning a shared variable is safe via copying before the lock is released.
    1614 Similar safety is offered by \emph{explicit} mechanisms like \CC RAII;
    1615 monitor \emph{implicit} safety ensures no programmer usage errors.
    1616 Furthermore, RAII mechanisms cannot handle complex synchronization within a monitor, where the monitor lock may not be released on function exit because it is passed to an unblocking thread;
     1759Aint x = { 0 }, y = { 0 }, z = { 5 }; // no mutex
     1760++x; ++y; ++z;     // mutex
     1761x = 2; y = i; z = k;  // mutex
     1762i = x; j = y; k = z;  // no mutex
     1763\end{cfa}
     1764\end{lrbox}
     1765
     1766\begin{lrbox}{\myboxB}
     1767\begin{java}[aboveskip=0pt,belowskip=0pt]
     1768class Aint {
     1769    private int cnt;
     1770    public Aint( int init ) { cnt = init; }
     1771    `synchronized` public int inc() { return ++cnt; }
     1772    `synchronized` public void set( int rhs ) {cnt=rhs;}
     1773    public int get() { return cnt; }
     1774}
     1775int i = 0, j = 0, k = 5;
     1776Aint x=new Aint(0), y=new Aint(0), z=new Aint(5);
     1777x.inc(); y.inc(); z.inc();
     1778x.set( 2 ); y.set( i ); z.set( k );
     1779i = x.get(); j = y.get(); k = z.get();
     1780\end{java}
     1781\end{lrbox}
     1782
     1783\subfloat[\CFA]{\label{f:AtomicCounterCFA}\usebox\myboxA}
     1784\hspace{3pt}
     1785\vrule
     1786\hspace{3pt}
     1787\subfloat[Java]{\label{f:AtomicCounterJava}\usebox\myboxB}
     1788\caption{Atomic counter}
     1789\label{f:AtomicCounter}
     1790\end{figure}
     1791
     1792Like Java, \CFA monitors have \newterm{multi-acquire} semantics so the thread in the monitor may acquire it multiple times without deadlock, allowing recursion and calling other interface functions.
     1793% \begin{cfa}
     1794% monitor M { ... } m;
     1795% void foo( M & mutex m ) { ... } $\C{// acquire mutual exclusion}$
     1796% void bar( M & mutex m ) { $\C{// acquire mutual exclusion}$
     1797%       ... `bar( m );` ... `foo( m );` ... $\C{// reacquire mutual exclusion}$
     1798% }
     1799% \end{cfa}
     1800\CFA monitors also ensure the monitor lock is released regardless of how an acquiring function ends, normal or exceptional, and returning a shared variable is safe via copying before the lock is released.
     1801Similar safety is offered by \emph{explicit} opt-in disciplines like \CC RAII versus the monitor \emph{implicit} language-enforced safety guarantee ensuring no programmer usage errors.
     1802However, RAII mechanisms cannot handle complex synchronization within a monitor, where the monitor lock may not be released on function exit because it is passed to an unblocking thread;
    16171803RAII is purely a mutual-exclusion mechanism (see Section~\ref{s:Scheduling}).
    16181804
    1619 
    1620 \subsection{Monitor Implementation}
     1805Both Java and \CFA use a keyword @mutex@/\lstinline[language=java]|synchronized| to designate functions that implicitly acquire/release the monitor lock on call/return providing mutual exclusion to the stared data.
     1806Non-designated functions provide no mutual exclusion for read-only access or as an interface to a multi-step protocol requiring several steps of acquiring and releasing the monitor.
     1807Monitor objects can be passed through multiple helper functions without acquiring mutual exclusion, until a designated function associated with the object is called.
     1808\CFA designated functions are marked by an explicitly parameter-only pointer/reference qualifier @mutex@ (discussed further in Section\ref{s:MutexAcquisition}).
     1809Whereas, Java designated members are marked with \lstinline[language=java]|synchronized| that applies to the implicit reference parameter @this@.
     1810In the example, the increment and setter operations need mutual exclusion while the read-only getter operation can be nonmutex if reading the implementation is atomic.
     1811
     1812
     1813\subsection{Monitor implementation}
    16211814
    16221815For the same design reasons, \CFA provides a custom @monitor@ type and a @trait@ to enforce and restrict the monitor-interface functions.
     
    16381831\end{tabular}
    16391832\end{cquote}
    1640 The @dtype@ property prevents \emph{implicit} copy operations and the @is_monitor@ trait provides no \emph{explicit} copy operations, so monitors must be passed by reference (pointer).
    1641 % Copying a lock is insecure because it is possible to copy an open lock and then use the open copy when the original lock is closed to simultaneously access the shared data.
    1642 % Copying a monitor is secure because both the lock and shared data are copies, but copying the shared data is meaningless because it no longer represents a unique entity.
    1643 Similarly, the function definitions ensures there is a mechanism to get (read) the monitor descriptor from its handle, and a special destructor to prevent deallocation if a thread using the shared data.
     1833The @dtype@ property prevents \emph{implicit} copy operations and the @is_monitor@ trait provides no \emph{explicit} copy operations, so monitors must be passed by reference or pointer.
     1834Similarly, the function definitions ensure there is a mechanism to read the monitor descriptor from its handle, and a special destructor to prevent deallocation if a thread is using the shared data.
    16441835The custom monitor type also inserts any locks needed to implement the mutual exclusion semantics.
    1645 
    1646 
    1647 \subsection{Mutex Acquisition}
     1836\CFA relies heavily on traits as an abstraction mechanism, so the @mutex@ qualifier prevents coincidentally matching of a monitor trait with a type that is not a monitor, similar to coincidental inheritance where a shape and playing card can both be drawable.
     1837
     1838
     1839\subsection{Mutex acquisition}
    16481840\label{s:MutexAcquisition}
    16491841
    1650 While the monitor lock provides mutual exclusion for shared data, there are implementation options for when and where the locking/unlocking occurs.
    1651 (Much of this discussion also applies to basic locks.)
    1652 For example, a monitor may be passed through multiple helper functions before it is necessary to acquire the monitor's mutual exclusion.
    1653 
    1654 The benefit of mandatory monitor qualifiers is self-documentation, but requiring both @mutex@ and \lstinline[morekeywords=nomutex]@nomutex@ for all monitor parameters is redundant.
    1655 Instead, the semantics has one qualifier as the default and the other required.
    1656 For example, make the safe @mutex@ qualifier the default because assuming \lstinline[morekeywords=nomutex]@nomutex@ may cause subtle errors.
    1657 Alternatively, make the unsafe \lstinline[morekeywords=nomutex]@nomutex@ qualifier the default because it is the \emph{normal} parameter semantics while @mutex@ parameters are rare.
    1658 Providing a default qualifier implies knowing whether a parameter is a monitor.
    1659 Since \CFA relies heavily on traits as an abstraction mechanism, types can coincidentally match the monitor trait but not be a monitor, similar to inheritance where a shape and playing card can both be drawable.
    1660 For this reason, \CFA requires programmers to identify the kind of parameter with the @mutex@ keyword and uses no keyword to mean \lstinline[morekeywords=nomutex]@nomutex@.
    1661 
    1662 The next semantic decision is establishing which parameter \emph{types} may be qualified with @mutex@.
    1663 The following has monitor parameter types that are composed of multiple objects.
    1664 \begin{cfa}
    1665 monitor M { ... }
     1842For object-oriented programming languages, the mutex property applies to one object, the implicit pointer/reference to the monitor type.
     1843Because \CFA uses a pointer qualifier, other possibilities exist, \eg:
     1844\begin{cfa}
     1845monitor M { ... };
    16661846int f1( M & mutex m ); $\C{// single parameter object}$
    16671847int f2( M * mutex m ); $\C{// single or multiple parameter object}$
     
    16691849int f4( stack( M * ) & mutex m ); $\C{// multiple parameters object}$
    16701850\end{cfa}
    1671 Function @f1@ has a single parameter object, while @f2@'s indirection could be a single or multi-element array, where static array size is often unknown in C.
    1672 Function @f3@ has a multiple object matrix, and @f4@ a multiple object data structure.
    1673 While shown shortly, multiple object acquisition is possible, but the number of objects must be statically known.
    1674 Therefore, \CFA only acquires one monitor per parameter with at most one level of indirection, excluding pointers as it is impossible to statically determine the size.
    1675 
    1676 For object-oriented monitors, \eg Java, calling a mutex member \emph{implicitly} acquires mutual exclusion of the receiver object, @`rec`.foo(...)@.
    1677 \CFA has no receiver, and hence, the explicit @mutex@ qualifier is used to specify which objects acquire mutual exclusion.
    1678 A positive consequence of this design decision is the ability to support multi-monitor functions,\footnote{
     1851Function @f1@ has a single object parameter, while functions @f2@ to @f4@ can be a single or multi-element parameter with statically unknown size.
     1852Because of the statically unknown size, \CFA only supports a single reference @mutex@ parameter, @f1@.
     1853
     1854The \CFA @mutex@ qualifier does allow the ability to support multimonitor functions,\footnote{
    16791855While object-oriented monitors can be extended with a mutex qualifier for multiple-monitor members, no prior example of this feature could be found.}
    1680 called \newterm{bulk acquire}.
    1681 \CFA guarantees acquisition order is consistent across calls to @mutex@ functions using the same monitors as arguments, so acquiring multiple monitors is safe from deadlock.
     1856where the number of acquisitions is statically known, called \newterm{bulk acquire}.
     1857\CFA guarantees bulk acquisition order is consistent across calls to @mutex@ functions using the same monitors as arguments, so acquiring multiple monitors in a bulk acquire is safe from deadlock.
    16821858Figure~\ref{f:BankTransfer} shows a trivial solution to the bank transfer problem~\cite{BankTransfer}, where two resources must be locked simultaneously, using \CFA monitors with implicit locking and \CC with explicit locking.
    16831859A \CFA programmer only has to manage when to acquire mutual exclusion;
     
    16991875void transfer( BankAccount & `mutex` my,
    17001876        BankAccount & `mutex` your, int me2you ) {
    1701 
     1877        // bulk acquire
    17021878        deposit( my, -me2you ); // debit
    17031879        deposit( your, me2you ); // credit
     
    17291905void transfer( BankAccount & my,
    17301906                        BankAccount & your, int me2you ) {
    1731         `scoped_lock lock( my.m, your.m );`
     1907        `scoped_lock lock( my.m, your.m );` // bulk acquire
    17321908        deposit( my, -me2you ); // debit
    17331909        deposit( your, me2you ); // credit
     
    17571933\end{figure}
    17581934
    1759 Users can still force the acquiring order by using @mutex@/\lstinline[morekeywords=nomutex]@nomutex@.
     1935Users can still force the acquiring order by using or not using @mutex@.
    17601936\begin{cfa}
    17611937void foo( M & mutex m1, M & mutex m2 ); $\C{// acquire m1 and m2}$
    1762 void bar( M & mutex m1, M & /* nomutex */ m2 ) { $\C{// acquire m1}$
     1938void bar( M & mutex m1, M & m2 ) { $\C{// only acquire m1}$
    17631939        ... foo( m1, m2 ); ... $\C{// acquire m2}$
    17641940}
    1765 void baz( M & /* nomutex */ m1, M & mutex m2 ) { $\C{// acquire m2}$
     1941void baz( M & m1, M & mutex m2 ) { $\C{// only acquire m2}$
    17661942        ... foo( m1, m2 ); ... $\C{// acquire m1}$
    17671943}
     
    18061982% There are many aspects of scheduling in a concurrency system, all related to resource utilization by waiting threads, \ie which thread gets the resource next.
    18071983% Different forms of scheduling include access to processors by threads (see Section~\ref{s:RuntimeStructureCluster}), another is access to a shared resource by a lock or monitor.
    1808 This section discusses monitor scheduling for waiting threads eligible for entry, \ie which thread gets the shared resource next. (See Section~\ref{s:RuntimeStructureCluster} for scheduling threads on virtual processors.)
    1809 While monitor mutual-exclusion provides safe access to shared data, the monitor data may indicate that a thread accessing it cannot proceed, \eg a bounded buffer may be full/empty so produce/consumer threads must block.
    1810 Leaving the monitor and trying again (busy waiting) is impractical for high-level programming.
    1811 Monitors eliminate busy waiting by providing synchronization to schedule threads needing access to the shared data, where threads block versus spinning.
     1984This section discusses scheduling for waiting threads eligible for monitor entry~\cite{Buhr95b}, \ie which user thread gets the shared resource next.
     1985(See Section~\ref{s:RuntimeStructureCluster} for scheduling kernel threads on virtual processors.)
     1986While monitor mutual-exclusion provides safe access to its shared data, the data may indicate a thread cannot proceed, \eg a bounded buffer may be full/\-empty so produce/consumer threads must block.
     1987Leaving the monitor and retrying (busy waiting) is impractical for high-level programming.
     1988
     1989Monitors eliminate busy waiting by providing synchronization within the monitor critical-section to schedule threads needing access to the shared data, where threads block versus spin.
    18121990Synchronization is generally achieved with internal~\cite{Hoare74} or external~\cite[\S~2.9.2]{uC++} scheduling.
    1813 \newterm{Internal scheduling} is characterized by each thread entering the monitor and making an individual decision about proceeding or blocking, while \newterm{external scheduling} is characterized by an entering thread making a decision about proceeding for itself and on behalf of other threads attempting entry.
    1814 Finally, \CFA monitors do not allow calling threads to barge ahead of signalled threads, which simplifies synchronization among threads in the monitor and increases correctness.
    1815 If barging is allowed, synchronization between a signaller and signallee is difficult, often requiring additional flags and multiple unblock/block cycles.
    1816 In fact, signals-as-hints is completely opposite from that proposed by Hoare in the seminal paper on monitors~\cite[p.~550]{Hoare74}.
     1991\newterm{Internal} largely schedules threads located \emph{inside} the monitor and is accomplished using condition variables with signal and wait.
     1992\newterm{External} largely schedules threads located \emph{outside} the monitor and is accomplished with the @waitfor@ statement.
     1993Note, internal scheduling has a small amount of external scheduling and vice versa, so the naming denotes where the majority of the block threads reside (inside or outside) for scheduling.
     1994For complex scheduling, the approaches can be combined, so there are threads waiting inside and outside.
     1995
     1996\CFA monitors do not allow calling threads to barge ahead of signaled threads via barging prevention, which simplifies synchronization among threads in the monitor and increases correctness.
     1997A direct consequence of this semantics is that unblocked waiting threads are not required to recheck the waiting condition, \ie waits are not in a starvation-prone busy-loop as required by the signals-as-hints style with barging.
     1998Preventing barging comes directly from Hoare's semantics in the seminal paper on monitors~\cite[p.~550]{Hoare74}.
    18171999% \begin{cquote}
    18182000% However, we decree that a signal operation be followed immediately by resumption of a waiting program, without possibility of an intervening procedure call from yet a third program.
    1819 % It is only in this way that a waiting program has an absolute guarantee that it can acquire the resource just released by the signalling program without any danger that a third program will interpose a monitor entry and seize the resource instead.~\cite[p.~550]{Hoare74}
     2001% It is only in this way that a waiting program has an absolute guarantee that it can acquire the resource just released by the signaling program without any danger that a third program will interpose a monitor entry and seize the resource instead.~\cite[p.~550]{Hoare74}
    18202002% \end{cquote}
    1821 Furthermore, \CFA concurrency has no spurious wakeup~\cite[\S~9]{Buhr05a}, which eliminates an implicit form of self barging.
    1822 Hence, a \CFA @wait@ statement is not enclosed in a @while@ loop retesting a blocking predicate, which can cause thread starvation due to barging.
    1823 
    1824 Figure~\ref{f:MonitorScheduling} shows general internal/external scheduling (for the bounded-buffer example in Figure~\ref{f:InternalExternalScheduling}).
    1825 External calling threads block on the calling queue, if the monitor is occupied, otherwise they enter in FIFO order.
    1826 Internal threads block on condition queues via @wait@ and reenter from the condition in FIFO order.
    1827 Alternatively, internal threads block on urgent from the @signal_block@ or @waitfor@, and reenter implicitly when the monitor becomes empty, \ie, the thread in the monitor exits or waits.
    1828 
    1829 There are three signalling mechanisms to unblock waiting threads to enter the monitor.
    1830 Note, signalling cannot have the signaller and signalled thread in the monitor simultaneously because of the mutual exclusion, so either the signaller or signallee can proceed.
    1831 For internal scheduling, threads are unblocked from condition queues using @signal@, where the signallee is moved to urgent and the signaller continues (solid line).
    1832 Multiple signals move multiple signallees to urgent until the condition is empty.
    1833 When the signaller exits or waits, a thread blocked on urgent is processed before calling threads to prevent barging.
    1834 (Java conceptually moves the signalled thread to the calling queue, and hence, allows barging.)
    1835 The alternative unblock is in the opposite order using @signal_block@, where the signaller is moved to urgent and the signallee continues (dashed line), and is implicitly unblocked from urgent when the signallee exits or waits.
    1836 
    1837 For external scheduling, the condition queues are not used;
    1838 instead threads are unblocked directly from the calling queue using @waitfor@ based on function names requesting mutual exclusion.
    1839 (The linear search through the calling queue to locate a particular call can be reduced to $O(1)$.)
    1840 The @waitfor@ has the same semantics as @signal_block@, where the signalled thread executes before the signallee, which waits on urgent.
    1841 Executing multiple @waitfor@s from different signalled functions causes the calling threads to move to urgent.
    1842 External scheduling requires urgent to be a stack, because the signaller expects to execute immediately after the specified monitor call has exited or waited.
    1843 Internal scheduling behaves the same for an urgent stack or queue, except for multiple signalling, where the threads unblock from urgent in reverse order from signalling.
    1844 If the restart order is important, multiple signalling by a signal thread can be transformed into daisy-chain signalling among threads, where each thread signals the next thread.
    1845 We tried both a stack for @waitfor@ and queue for signalling, but that resulted in complex semantics about which thread enters next.
    1846 Hence, \CFA uses a single urgent stack to correctly handle @waitfor@ and adequately support both forms of signalling.
     2003Furthermore, \CFA concurrency has no spurious wakeup~\cite[\S~9]{Buhr05a}, which eliminates an implicit self barging.
     2004
     2005Monitor mutual-exclusion means signaling cannot have the signaller and signaled thread in the monitor simultaneously, so only the signaller or signallee can proceed and the other waits on an implicit urgent list~\cite[p.~551]{Hoare74}.
     2006Figure~\ref{f:MonitorScheduling} shows internal and external scheduling for the bounded-buffer examples in Figure~\ref{f:GenericBoundedBuffer}.
     2007For internal scheduling in Figure~\ref{f:BBInt}, the @signal@ moves the signallee, front thread of the specified condition queue, to the urgent list (see Figure~\ref{f:MonitorScheduling}) and the signaller continues (solid line).
     2008Multiple signals move multiple signallees to urgent until the condition queue is empty.
     2009When the signaller exits or waits, a thread is implicitly unblocked from urgent, if available, before unblocking a calling thread to prevent barging.
     2010(Java conceptually moves the signaled thread to the calling queue, and hence, allows barging.)
     2011Signal is used when the signaller is providing the cooperation needed by the signallee, \eg creating an empty slot in a buffer for a producer, and the signaller immediately exits the monitor to run concurrently consuming the buffer element, and passes control of the monitor to the signaled thread, which can immediately take advantage of the state change.
     2012Specifically, the @wait@ function atomically blocks the calling thread and implicitly releases the monitor lock(s) for all monitors in the function's parameter list.
     2013Signalling is unconditional because signaling an empty condition queue does nothing.
     2014It is common to declare condition queues as monitor fields to prevent shared access, hence no locking is required for access as the queues are protected by the monitor lock.
     2015In \CFA, a condition queue can be created and stored independently.
    18472016
    18482017\begin{figure}
     
    18622031\end{figure}
    18632032
    1864 Figure~\ref{f:BBInt} shows a \CFA generic bounded-buffer with internal scheduling, where producers/consumers enter the monitor, detect the buffer is full/empty, and block on an appropriate condition variable, @full@/@empty@.
    1865 The @wait@ function atomically blocks the calling thread and implicitly releases the monitor lock(s) for all monitors in the function's parameter list.
    1866 The appropriate condition variable is signalled to unblock an opposite kind of thread after an element is inserted/removed from the buffer.
    1867 Signalling is unconditional, because signalling an empty condition variable does nothing.
    1868 It is common to declare condition variables as monitor fields to prevent shared access, hence no locking is required for access as the conditions are protected by the monitor lock.
    1869 In \CFA, a condition variable can be created/stored independently.
    1870 % To still prevent expensive locking on access, a condition variable is tied to a \emph{group} of monitors on first use, called \newterm{branding}, resulting in a low-cost boolean test to detect sharing from other monitors.
    1871 
    1872 % Signalling semantics cannot have the signaller and signalled thread in the monitor simultaneously, which means:
    1873 % \begin{enumerate}
    1874 % \item
    1875 % The signalling thread returns immediately and the signalled thread continues.
    1876 % \item
    1877 % The signalling thread continues and the signalled thread is marked for urgent unblocking at the next scheduling point (exit/wait).
    1878 % \item
    1879 % The signalling thread blocks but is marked for urgent unblocking at the next scheduling point and the signalled thread continues.
    1880 % \end{enumerate}
    1881 % The first approach is too restrictive, as it precludes solving a reasonable class of problems, \eg dating service (see Figure~\ref{f:DatingService}).
    1882 % \CFA supports the next two semantics as both are useful.
    1883 
    18842033\begin{figure}
    18852034\centering
     
    18932042                T elements[10];
    18942043        };
    1895         void ?{}( Buffer(T) & buffer ) with(buffer) {
     2044        void ?{}( Buffer(T) & buf ) with(buf) {
    18962045                front = back = count = 0;
    18972046        }
    1898         void insert( Buffer(T) & mutex buffer, T elem )
    1899                                 with(buffer) {
    1900                 if ( count == 10 ) `wait( empty )`;
    1901                 // insert elem into buffer
     2047
     2048        void insert(Buffer(T) & mutex buf, T elm) with(buf){
     2049                if ( count == 10 ) `wait( empty )`; // full ?
     2050                // insert elm into buf
    19022051                `signal( full )`;
    19032052        }
    1904         T remove( Buffer(T) & mutex buffer ) with(buffer) {
    1905                 if ( count == 0 ) `wait( full )`;
    1906                 // remove elem from buffer
     2053        T remove( Buffer(T) & mutex buf ) with(buf) {
     2054                if ( count == 0 ) `wait( full )`; // empty ?
     2055                // remove elm from buf
    19072056                `signal( empty )`;
    1908                 return elem;
     2057                return elm;
    19092058        }
    19102059}
    19112060\end{cfa}
    19122061\end{lrbox}
    1913 
    1914 % \newbox\myboxB
    1915 % \begin{lrbox}{\myboxB}
    1916 % \begin{cfa}[aboveskip=0pt,belowskip=0pt]
    1917 % forall( otype T ) { // distribute forall
    1918 %       monitor Buffer {
    1919 %
    1920 %               int front, back, count;
    1921 %               T elements[10];
    1922 %       };
    1923 %       void ?{}( Buffer(T) & buffer ) with(buffer) {
    1924 %               [front, back, count] = 0;
    1925 %       }
    1926 %       T remove( Buffer(T) & mutex buffer ); // forward
    1927 %       void insert( Buffer(T) & mutex buffer, T elem )
    1928 %                               with(buffer) {
    1929 %               if ( count == 10 ) `waitfor( remove, buffer )`;
    1930 %               // insert elem into buffer
    1931 %
    1932 %       }
    1933 %       T remove( Buffer(T) & mutex buffer ) with(buffer) {
    1934 %               if ( count == 0 ) `waitfor( insert, buffer )`;
    1935 %               // remove elem from buffer
    1936 %
    1937 %               return elem;
    1938 %       }
    1939 % }
    1940 % \end{cfa}
    1941 % \end{lrbox}
    19422062
    19432063\newbox\myboxB
    19442064\begin{lrbox}{\myboxB}
    19452065\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     2066forall( otype T ) { // distribute forall
     2067        monitor Buffer {
     2068
     2069                int front, back, count;
     2070                T elements[10];
     2071        };
     2072        void ?{}( Buffer(T) & buf ) with(buf) {
     2073                front = back = count = 0;
     2074        }
     2075        T remove( Buffer(T) & mutex buf ); // forward
     2076        void insert(Buffer(T) & mutex buf, T elm) with(buf){
     2077                if ( count == 10 ) `waitfor( remove : buf )`;
     2078                // insert elm into buf
     2079
     2080        }
     2081        T remove( Buffer(T) & mutex buf ) with(buf) {
     2082                if ( count == 0 ) `waitfor( insert : buf )`;
     2083                // remove elm from buf
     2084
     2085                return elm;
     2086        }
     2087}
     2088\end{cfa}
     2089\end{lrbox}
     2090
     2091\subfloat[Internal scheduling]{\label{f:BBInt}\usebox\myboxA}
     2092\hspace{1pt}
     2093\vrule
     2094\hspace{3pt}
     2095\subfloat[External scheduling]{\label{f:BBExt}\usebox\myboxB}
     2096
     2097\caption{Generic bounded buffer}
     2098\label{f:GenericBoundedBuffer}
     2099\end{figure}
     2100
     2101The @signal_block@ provides the opposite unblocking order, where the signaller is moved to urgent and the signallee continues and a thread is implicitly unblocked from urgent when the signallee exits or waits (dashed line)~\cite[p.~551]{Hoare74}.
     2102Signal block is used when the signallee is providing the cooperation needed by the signaller, \eg if the buffer is removed and a producer hands off an item to a consumer as in Figure~\ref{f:DatingSignalBlock}, so the signaller must wait until the signallee unblocks, provides the cooperation, exits the monitor to run concurrently, and passes control of the monitor to the signaller, which can immediately take advantage of the state change.
     2103Using @signal@ or @signal_block@ can be a dynamic decision based on whether the thread providing the cooperation arrives before or after the thread needing the cooperation.
     2104
     2105For external scheduling in Figure~\ref{f:BBExt}, the internal scheduling is replaced, eliminating condition queues and @signal@/@wait@ (cases where it cannot are discussed shortly), and has existed in the programming language Ada for almost 40 years with variants in other languages~\cite{SR,ConcurrentC++,uC++}.
     2106While prior languages use external scheduling solely for thread interaction, \CFA generalizes it to both monitors and threads.
     2107External scheduling allows waiting for events from other threads while restricting unrelated events, that would otherwise have to wait on condition queues in the monitor.
     2108Scheduling is controlled by the @waitfor@ statement, which atomically blocks the calling thread, releases the monitor lock, and restricts the function calls that can next acquire mutual exclusion.
     2109Specifically, a thread calling the monitor is unblocked directly from the calling queue based on function names that can fulfill the cooperation required by the signaller.
     2110(The linear search through the calling queue to locate a particular call can be reduced to $O(1)$.)
     2111Hence, the @waitfor@ has the same semantics as @signal_block@, where the signallee thread from the calling queue executes before the signaller, which waits on urgent.
     2112Now when a producer/consumer detects a full/empty buffer, the necessary cooperation for continuation is specified by indicating the next function call that can occur.
     2113For example, a producer detecting a full buffer must have cooperation from a consumer to remove an item so function @remove@ is accepted, which prevents producers from entering the monitor, and after a consumer calls @remove@, the producer waiting on urgent is \emph{implicitly} unblocked because it can now continue its insert operation.
     2114Hence, this mechanism is done in terms of control flow, next call, versus in terms of data, channels, as in Go and Rust @select@.
     2115While both mechanisms have strengths and weaknesses, \CFA uses the control-flow mechanism to be consistent with other language features.
     2116
     2117Figure~\ref{f:ReadersWriterLock} shows internal and external scheduling for a readers/writer lock with no barging and threads are serviced in FIFO order to eliminate staleness and freshness among the reader/writer threads.
     2118For internal scheduling in Figure~\ref{f:RWInt}, the readers and writers wait on the same condition queue in FIFO order, making it impossible to tell if a waiting thread is a reader or writer.
     2119To clawback the kind of thread, a \CFA condition can store user data in the node for a blocking thread at the @wait@, \ie whether the thread is a @READER@ or @WRITER@.
     2120An unblocked reader thread checks if the thread at the front of the queue is a reader and unblock it, \ie the readers daisy-chain signal the next group of readers demarcated by the next writer or end of the queue.
     2121For external scheduling in Figure~\ref{f:RWExt}, a waiting reader checks if a writer is using the resource, and if so, restricts further calls until the writer exits by calling @EndWrite@.
     2122The writer does a similar action for each reader or writer using the resource.
     2123Note, no new calls to @StartRead@/@StartWrite@ may occur when waiting for the call to @EndRead@/@EndWrite@.
     2124
     2125\begin{figure}
     2126\centering
     2127\newbox\myboxA
     2128\begin{lrbox}{\myboxA}
     2129\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     2130enum RW { READER, WRITER };
    19462131monitor ReadersWriter {
    1947         int rcnt, wcnt; // readers/writer using resource
     2132        int rcnt, wcnt; // readers/writer using resource
     2133        `condition RWers;`
    19482134};
    19492135void ?{}( ReadersWriter & rw ) with(rw) {
     
    19522138void EndRead( ReadersWriter & mutex rw ) with(rw) {
    19532139        rcnt -= 1;
     2140        if ( rcnt == 0 ) `signal( RWers )`;
    19542141}
    19552142void EndWrite( ReadersWriter & mutex rw ) with(rw) {
    19562143        wcnt = 0;
     2144        `signal( RWers );`
    19572145}
    19582146void StartRead( ReadersWriter & mutex rw ) with(rw) {
    1959         if ( wcnt > 0 ) `waitfor( EndWrite, rw );`
     2147        if ( wcnt !=0 || ! empty( RWers ) )
     2148                `wait( RWers, READER )`;
    19602149        rcnt += 1;
     2150        if ( ! empty(RWers) && `front(RWers) == READER` )
     2151                `signal( RWers )`;  // daisy-chain signaling
    19612152}
    19622153void StartWrite( ReadersWriter & mutex rw ) with(rw) {
    1963         if ( wcnt > 0 ) `waitfor( EndWrite, rw );`
    1964         else while ( rcnt > 0 ) `waitfor( EndRead, rw );`
     2154        if ( wcnt != 0 || rcnt != 0 ) `wait( RWers, WRITER )`;
     2155
    19652156        wcnt = 1;
    19662157}
    1967 
    19682158\end{cfa}
    19692159\end{lrbox}
    19702160
    1971 \subfloat[Generic bounded buffer, internal scheduling]{\label{f:BBInt}\usebox\myboxA}
    1972 \hspace{3pt}
     2161\newbox\myboxB
     2162\begin{lrbox}{\myboxB}
     2163\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     2164
     2165monitor ReadersWriter {
     2166        int rcnt, wcnt; // readers/writer using resource
     2167
     2168};
     2169void ?{}( ReadersWriter & rw ) with(rw) {
     2170        rcnt = wcnt = 0;
     2171}
     2172void EndRead( ReadersWriter & mutex rw ) with(rw) {
     2173        rcnt -= 1;
     2174
     2175}
     2176void EndWrite( ReadersWriter & mutex rw ) with(rw) {
     2177        wcnt = 0;
     2178
     2179}
     2180void StartRead( ReadersWriter & mutex rw ) with(rw) {
     2181        if ( wcnt > 0 ) `waitfor( EndWrite : rw );`
     2182
     2183        rcnt += 1;
     2184
     2185
     2186}
     2187void StartWrite( ReadersWriter & mutex rw ) with(rw) {
     2188        if ( wcnt > 0 ) `waitfor( EndWrite : rw );`
     2189        else while ( rcnt > 0 ) `waitfor( EndRead : rw );`
     2190        wcnt = 1;
     2191}
     2192\end{cfa}
     2193\end{lrbox}
     2194
     2195\subfloat[Internal scheduling]{\label{f:RWInt}\usebox\myboxA}
     2196\hspace{1pt}
    19732197\vrule
    19742198\hspace{3pt}
    1975 \subfloat[Readers / writer lock, external scheduling]{\label{f:RWExt}\usebox\myboxB}
    1976 
    1977 \caption{Internal / external scheduling}
    1978 \label{f:InternalExternalScheduling}
     2199\subfloat[External scheduling]{\label{f:RWExt}\usebox\myboxB}
     2200
     2201\caption{Readers / writer lock}
     2202\label{f:ReadersWriterLock}
    19792203\end{figure}
    19802204
    1981 Figure~\ref{f:BBInt} can be transformed into external scheduling by removing the condition variables and signals/waits, and adding the following lines at the locations of the current @wait@s in @insert@/@remove@, respectively.
    1982 \begin{cfa}[aboveskip=2pt,belowskip=1pt]
    1983 if ( count == 10 ) `waitfor( remove, buffer )`;       |      if ( count == 0 ) `waitfor( insert, buffer )`;
    1984 \end{cfa}
    1985 Here, the producers/consumers detects a full/\-empty buffer and prevents more producers/consumers from entering the monitor until there is a free/empty slot in the buffer.
    1986 External scheduling is controlled by the @waitfor@ statement, which atomically blocks the calling thread, releases the monitor lock, and restricts the function calls that can next acquire mutual exclusion.
    1987 If the buffer is full, only calls to @remove@ can acquire the buffer, and if the buffer is empty, only calls to @insert@ can acquire the buffer.
    1988 Threads calling excluded functions block outside of (external to) the monitor on the calling queue, versus blocking on condition queues inside of (internal to) the monitor.
    1989 Figure~\ref{f:RWExt} shows a readers/writer lock written using external scheduling, where a waiting reader detects a writer using the resource and restricts further calls until the writer exits by calling @EndWrite@.
    1990 The writer does a similar action for each reader or writer using the resource.
    1991 Note, no new calls to @StarRead@/@StartWrite@ may occur when waiting for the call to @EndRead@/@EndWrite@.
    1992 External scheduling allows waiting for events from other threads while restricting unrelated events, that would otherwise have to wait on conditions in the monitor.
    1993 The mechnaism can be done in terms of control flow, \eg Ada @accept@ or \uC @_Accept@, or in terms of data, \eg Go @select@ on channels.
    1994 While both mechanisms have strengths and weaknesses, this project uses the control-flow mechanism to be consistent with other language features.
    1995 % Two challenges specific to \CFA for external scheduling are loose object-definitions (see Section~\ref{s:LooseObjectDefinitions}) and multiple-monitor functions (see Section~\ref{s:Multi-MonitorScheduling}).
    1996 
    1997 Figure~\ref{f:DatingService} shows a dating service demonstrating non-blocking and blocking signalling.
    1998 The dating service matches girl and boy threads with matching compatibility codes so they can exchange phone numbers.
    1999 A thread blocks until an appropriate partner arrives.
    2000 The complexity is exchanging phone numbers in the monitor because of the mutual-exclusion property.
    2001 For signal scheduling, the @exchange@ condition is necessary to block the thread finding the match, while the matcher unblocks to take the opposite number, post its phone number, and unblock the partner.
    2002 For signal-block scheduling, the implicit urgent-queue replaces the explict @exchange@-condition and @signal_block@ puts the finding thread on the urgent condition and unblocks the matcher.
    2003 The dating service is an example of a monitor that cannot be written using external scheduling because it requires knowledge of calling parameters to make scheduling decisions, and parameters of waiting threads are unavailable;
    2004 as well, an arriving thread may not find a partner and must wait, which requires a condition variable, and condition variables imply internal scheduling.
    2005 Furthermore, barging corrupts the dating service during an exchange because a barger may also match and change the phone numbers, invalidating the previous exchange phone number.
    2006 Putting loops around the @wait@s does not correct the problem;
    2007 the simple solution must be restructured to account for barging.
     2205Finally, external scheduling requires urgent to be a stack, because the signaller expects to execute immediately after the specified monitor call has exited or waited.
     2206Internal scheduling performing multiple signaling results in unblocking from urgent in the reverse order from signaling.
     2207It is rare for the unblocking order to be important as an unblocked thread can be time-sliced immediately after leaving the monitor.
     2208If the unblocking order is important, multiple signaling can be restructured into daisy-chain signaling, where each thread signals the next thread.
     2209Hence, \CFA uses a single urgent stack to correctly handle @waitfor@ and adequately support both forms of signaling.
     2210(Advanced @waitfor@ features are discussed in Section~\ref{s:ExtendedWaitfor}.)
    20082211
    20092212\begin{figure}
     
    20192222};
    20202223int girl( DS & mutex ds, int phNo, int ccode ) {
    2021         if ( is_empty( Boys[ccode] ) ) {
     2224        if ( empty( Boys[ccode] ) ) {
    20222225                wait( Girls[ccode] );
    20232226                GirlPhNo = phNo;
     
    20462249};
    20472250int girl( DS & mutex ds, int phNo, int ccode ) {
    2048         if ( is_empty( Boys[ccode] ) ) { // no compatible
     2251        if ( empty( Boys[ccode] ) ) { // no compatible
    20492252                wait( Girls[ccode] ); // wait for boy
    20502253                GirlPhNo = phNo; // make phone number available
     
    20662269\qquad
    20672270\subfloat[\lstinline@signal_block@]{\label{f:DatingSignalBlock}\usebox\myboxB}
    2068 \caption{Dating service}
    2069 \label{f:DatingService}
     2271\caption{Dating service Monitor}
     2272\label{f:DatingServiceMonitor}
    20702273\end{figure}
    20712274
    2072 In summation, for internal scheduling, non-blocking signalling (as in the producer/consumer example) is used when the signaller is providing the cooperation for a waiting thread;
    2073 the signaller enters the monitor and changes state, detects a waiting threads that can use the state, performs a non-blocking signal on the condition queue for the waiting thread, and exits the monitor to run concurrently.
    2074 The waiter unblocks next from the urgent queue, uses/takes the state, and exits the monitor.
    2075 Blocking signal is the reverse, where the waiter is providing the cooperation for the signalling thread;
    2076 the signaller enters the monitor, detects a waiting thread providing the necessary state, performs a blocking signal to place it on the urgent queue and unblock the waiter.
    2077 The waiter changes state and exits the monitor, and the signaller unblocks next from the urgent queue to use/take the state.
    2078 
    2079 Both internal and external scheduling extend to multiple monitors in a natural way.
     2275Figure~\ref{f:DatingServiceMonitor} shows a dating service demonstrating nonblocking and blocking signaling.
     2276The dating service matches girl and boy threads with matching compatibility codes so they can exchange phone numbers.
     2277A thread blocks until an appropriate partner arrives.
     2278The complexity is exchanging phone numbers in the monitor because of the mutual-exclusion property.
     2279For signal scheduling, the @exchange@ condition is necessary to block the thread finding the match, while the matcher unblocks to take the opposite number, post its phone number, and unblock the partner.
     2280For signal-block scheduling, the implicit urgent-queue replaces the explicit @exchange@-condition and @signal_block@ puts the finding thread on the urgent stack and unblocks the matcher.
     2281Note, barging corrupts the dating service during an exchange because a barger may also match and change the phone numbers, invalidating the previous exchange phone number.
     2282This situation shows rechecking the waiting condition and waiting again (signals-as-hints) fails, requiring significant restructured to account for barging.
     2283
     2284Given external and internal scheduling, what guidelines can a programmer use to select between them?
     2285In general, external scheduling is easier to understand and code because only the next logical action (mutex function(s)) is stated, and the monitor implicitly handles all the details.
     2286Therefore, there are no condition variables, and hence, no wait and signal, which reduces coding complexity and synchronization errors.
     2287If external scheduling is simpler than internal, why not use it all the time?
     2288Unfortunately, external scheduling cannot be used if: scheduling depends on parameter value(s) or scheduling must block across an unknown series of calls on a condition variable, \ie internal scheduling.
     2289For example, the dating service cannot be written using external scheduling.
     2290First, scheduling requires knowledge of calling parameters to make matching decisions and parameters of calling threads are unavailable within the monitor.
     2291Specifically, a thread within the monitor cannot examine the @ccode@ of threads waiting on the calling queue to determine if there is a matching partner.
     2292(Similarly, if the bounded buffer or readers/writer are restructured with a single interface function with a parameter denoting producer/consumer or reader/write, they cannot be solved with external scheduling.)
     2293Second, a scheduling decision may be delayed across an unknown number of calls when there is no immediate match so the thread in the monitor must block on a condition.
     2294Specifically, if a thread determines there is no opposite calling thread with the same @ccode@, it must wait an unknown period until a matching thread arrives.
     2295For complex synchronization, both external and internal scheduling can be used to take advantage of best of properties of each.
     2296
     2297Finally, both internal and external scheduling extend to multiple monitors in a natural way.
    20802298\begin{cquote}
    2081 \begin{tabular}{@{}l@{\hspace{3\parindentlnth}}l@{}}
     2299\begin{tabular}{@{}l@{\hspace{2\parindentlnth}}l@{}}
    20822300\begin{cfa}
    20832301monitor M { `condition e`; ... };
     
    20902308&
    20912309\begin{cfa}
    2092 void rtn$\(_1\)$( M & mutex m1, M & mutex m2 );
     2310void rtn$\(_1\)$( M & mutex m1, M & mutex m2 ); // overload rtn
    20932311void rtn$\(_2\)$( M & mutex m1 );
    20942312void bar( M & mutex m1, M & mutex m2 ) {
    2095         ... waitfor( `rtn` ); ...       // $\LstCommentStyle{waitfor( rtn\(_1\), m1, m2 )}$
    2096         ... waitfor( `rtn, m1` ); ... // $\LstCommentStyle{waitfor( rtn\(_2\), m1 )}$
     2313        ... waitfor( `rtn`${\color{red}\(_1\)}$ ); ...       // $\LstCommentStyle{waitfor( rtn\(_1\) : m1, m2 )}$
     2314        ... waitfor( `rtn${\color{red}\(_2\)}$ : m1` ); ...
    20972315}
    20982316\end{cfa}
     
    21002318\end{cquote}
    21012319For @wait( e )@, the default semantics is to atomically block the signaller and release all acquired mutex parameters, \ie @wait( e, m1, m2 )@.
    2102 To override the implicit multi-monitor wait, specific mutex parameter(s) can be specified, \eg @wait( e, m1 )@.
    2103 Wait cannot statically verifies the released monitors are the acquired mutex-parameters without disallowing separately compiled helper functions calling @wait@.
    2104 While \CC supports bulk locking, @wait@ only accepts a single lock for a condition variable, so bulk locking with condition variables is asymmetric.
     2320To override the implicit multimonitor wait, specific mutex parameter(s) can be specified, \eg @wait( e, m1 )@.
     2321Wait cannot statically verify the released monitors are the acquired mutex-parameters without disallowing separately compiled helper functions calling @wait@.
     2322While \CC supports bulk locking, @wait@ only accepts a single lock for a condition queue, so bulk locking with condition queues is asymmetric.
    21052323Finally, a signaller,
    21062324\begin{cfa}
     
    21092327}
    21102328\end{cfa}
    2111 must have acquired at least the same locks as the waiting thread signalled from a condition queue to allow the locks to be passed, and hence, prevent barging.
    2112 
    2113 Similarly, for @waitfor( rtn )@, the default semantics is to atomically block the acceptor and release all acquired mutex parameters, \ie @waitfor( rtn, m1, m2 )@.
    2114 To override the implicit multi-monitor wait, specific mutex parameter(s) can be specified, \eg @waitfor( rtn, m1 )@.
    2115 @waitfor@ does statically verify the monitor types passed are the same as the acquired mutex-parameters of the given function or function pointer, hence the function (pointer) prototype must be accessible.
     2329must have acquired at least the same locks as the waiting thread signaled from a condition queue to allow the locks to be passed, and hence, prevent barging.
     2330
     2331Similarly, for @waitfor( rtn )@, the default semantics is to atomically block the acceptor and release all acquired mutex parameters, \ie @waitfor( rtn : m1, m2 )@.
     2332To override the implicit multimonitor wait, specific mutex parameter(s) can be specified, \eg @waitfor( rtn : m1 )@.
     2333@waitfor@ does statically verify the monitor types passed are the same as the acquired mutex-parameters of the given function or function pointer, hence the prototype must be accessible.
    21162334% When an overloaded function appears in an @waitfor@ statement, calls to any function with that name are accepted.
    2117 % The rationale is that members with the same name should perform a similar function, and therefore, all should be eligible to accept a call.
     2335% The rationale is that functions with the same name should perform a similar actions, and therefore, all should be eligible to accept a call.
    21182336Overloaded functions can be disambiguated using a cast
    21192337\begin{cfa}
    21202338void rtn( M & mutex m );
    21212339`int` rtn( M & mutex m );
    2122 waitfor( (`int` (*)( M & mutex ))rtn, m );
    2123 \end{cfa}
    2124 
    2125 The ability to release a subset of acquired monitors can result in a \newterm{nested monitor}~\cite{Lister77} deadlock.
     2340waitfor( (`int` (*)( M & mutex ))rtn : m );
     2341\end{cfa}
     2342
     2343The ability to release a subset of acquired monitors can result in a \newterm{nested monitor}~\cite{Lister77} deadlock (see Section~\ref{s:MutexAcquisition}).
    21262344\begin{cfa}
    21272345void foo( M & mutex m1, M & mutex m2 ) {
    2128         ... wait( `e, m1` ); ...                                $\C{// release m1, keeping m2 acquired )}$
    2129 void bar( M & mutex m1, M & mutex m2 ) {        $\C{// must acquire m1 and m2 )}$
     2346        ... wait( `e, m1` ); ...                                $\C{// release m1, keeping m2 acquired}$
     2347void bar( M & mutex m1, M & mutex m2 ) {        $\C{// must acquire m1 and m2}$
    21302348        ... signal( `e` ); ...
    21312349\end{cfa}
    2132 The @wait@ only releases @m1@ so the signalling thread cannot acquire @m1@ and @m2@ to enter @bar@ and @signal@ the condition.
    2133 While deadlock can occur with multiple/nesting acquisition, this is a consequence of locks, and by extension monitors, not being perfectly composable.
    2134 
     2350The @wait@ only releases @m1@ so the signaling thread cannot acquire @m1@ and @m2@ to enter @bar@ and @signal@ the condition.
     2351While deadlock can occur with multiple/nesting acquisition, this is a consequence of locks, and by extension monitor locking is not perfectly composable.
    21352352
    21362353
    21372354\subsection{\texorpdfstring{Extended \protect\lstinline@waitfor@}{Extended waitfor}}
     2355\label{s:ExtendedWaitfor}
    21382356
    21392357Figure~\ref{f:ExtendedWaitfor} shows the extended form of the @waitfor@ statement to conditionally accept one of a group of mutex functions, with an optional statement to be performed \emph{after} the mutex function finishes.
    2140 For a @waitfor@ clause to be executed, its @when@ must be true and an outstanding call to its corresponding member(s) must exist.
     2358For a @waitfor@ clause to be executed, its @when@ must be true and an outstanding call to its corresponding function(s) must exist.
    21412359The \emph{conditional-expression} of a @when@ may call a function, but the function must not block or context switch.
    2142 If there are multiple acceptable mutex calls, selection occurs top-to-bottom (prioritized) among the @waitfor@ clauses, whereas some programming languages with similar mechanisms accept nondeterministically for this case, \eg Go \lstinline[morekeywords=select]@select@.
    2143 If some accept guards are true and there are no outstanding calls to these members, the acceptor is blocked until a call to one of these members is made.
     2360If there are multiple acceptable mutex calls, selection is prioritized top-to-bottom among the @waitfor@ clauses, whereas some programming languages with similar mechanisms accept nondeterministically for this case, \eg Go \lstinline[morekeywords=select]@select@.
     2361If some accept guards are true and there are no outstanding calls to these functions, the acceptor is blocked until a call to one of these functions is made.
    21442362If there is a @timeout@ clause, it provides an upper bound on waiting.
    21452363If all the accept guards are false, the statement does nothing, unless there is a terminating @else@ clause with a true guard, which is executed instead.
    21462364Hence, the terminating @else@ clause allows a conditional attempt to accept a call without blocking.
    21472365If both @timeout@ and @else@ clause are present, the @else@ must be conditional, or the @timeout@ is never triggered.
    2148 There is also a traditional future wait queue (not shown) (\eg Microsoft (@WaitForMultipleObjects@)), to wait for a specified number of future elements in the queue.
     2366% There is also a traditional future wait queue (not shown) (\eg Microsoft @WaitForMultipleObjects@), to wait for a specified number of future elements in the queue.
     2367Finally, there is a shorthand for specifying multiple functions using the same set of monitors: @waitfor( f, g, h : m1, m2, m3 )@.
    21492368
    21502369\begin{figure}
     
    21522371\begin{cfa}
    21532372`when` ( $\emph{conditional-expression}$ )      $\C{// optional guard}$
    2154         waitfor( $\emph{mutex-member-name}$ ) $\emph{statement}$ $\C{// action after call}$
     2373        waitfor( $\emph{mutex-function-name}$ ) $\emph{statement}$ $\C{// action after call}$
    21552374`or` `when` ( $\emph{conditional-expression}$ ) $\C{// any number of functions}$
    2156         waitfor( $\emph{mutex-member-name}$ ) $\emph{statement}$
     2375        waitfor( $\emph{mutex-function-name}$ ) $\emph{statement}$
    21572376`or`    ...
    21582377`when` ( $\emph{conditional-expression}$ ) $\C{// optional guard}$
     
    21722391The left example only accepts @mem1@ if @C1@ is true or only @mem2@ if @C2@ is true.
    21732392The right example accepts either @mem1@ or @mem2@ if @C1@ and @C2@ are true.
    2174 
    2175 An interesting use of @waitfor@ is accepting the @mutex@ destructor to know when an object is deallocated, \eg assume the bounded buffer is restructred from a monitor to a thread with the following @main@.
     2393Hence, the @waitfor@ has parallel semantics, accepting any true @when@ clause.
     2394
     2395An interesting use of @waitfor@ is accepting the @mutex@ destructor to know when an object is deallocated, \eg assume the bounded buffer is restructured from a monitor to a thread with the following @main@.
    21762396\begin{cfa}
    21772397void main( Buffer(T) & buffer ) with(buffer) {
    21782398        for () {
    2179                 `waitfor( ^?{}, buffer )` break;
    2180                 or when ( count != 20 ) waitfor( insert, buffer ) { ... }
    2181                 or when ( count != 0 ) waitfor( remove, buffer ) { ... }
     2399                `waitfor( ^?{} : buffer )` break;
     2400                or when ( count != 20 ) waitfor( insert : buffer ) { ... }
     2401                or when ( count != 0 ) waitfor( remove : buffer ) { ... }
    21822402        }
    21832403        // clean up
     
    21922412
    21932413
    2194 \subsection{Bulk Barging Prevention}
    2195 
    2196 Figure~\ref{f:BulkBargingPrevention} shows \CFA code where bulk acquire adds complexity to the internal-signalling semantics.
     2414\subsection{Bulk barging prevention}
     2415
     2416Figure~\ref{f:BulkBargingPrevention} shows \CFA code where bulk acquire adds complexity to the internal-signaling semantics.
    21972417The complexity begins at the end of the inner @mutex@ statement, where the semantics of internal scheduling need to be extended for multiple monitors.
    21982418The problem is that bulk acquire is used in the inner @mutex@ statement where one of the monitors is already acquired.
    2199 When the signalling thread reaches the end of the inner @mutex@ statement, it should transfer ownership of @m1@ and @m2@ to the waiting threads to prevent barging into the outer @mutex@ statement by another thread.
    2200 However, both the signalling and waiting threads W1 and W2 need some subset of monitors @m1@ and @m2@.
     2419When the signaling thread reaches the end of the inner @mutex@ statement, it should transfer ownership of @m1@ and @m2@ to the waiting threads to prevent barging into the outer @mutex@ statement by another thread.
     2420However, both the signaling and waiting threads W1 and W2 need some subset of monitors @m1@ and @m2@.
    22012421\begin{cquote}
    22022422condition c: (order 1) W2(@m2@), W1(@m1@,@m2@)\ \ \ or\ \ \ (order 2) W1(@m1@,@m2@), W2(@m2@) \\
     
    22652485\end{figure}
    22662486
    2267 One scheduling solution is for the signaller S to keep ownership of all locks until the last lock is ready to be transferred, because this semantics fits most closely to the behaviour of single-monitor scheduling.
    2268 However, this solution is inefficient if W2 waited first and can be immediate passed @m2@ when released, while S retains @m1@ until completion of the outer mutex statement.
     2487One scheduling solution is for the signaller S to keep ownership of all locks until the last lock is ready to be transferred, because this semantics fits most closely to the behavior of single-monitor scheduling.
     2488However, this solution is inefficient if W2 waited first and immediate passed @m2@ when released, while S retains @m1@ until completion of the outer mutex statement.
    22692489If W1 waited first, the signaller must retain @m1@ amd @m2@ until completion of the outer mutex statement and then pass both to W1.
    22702490% Furthermore, there is an execution sequence where the signaller always finds waiter W2, and hence, waiter W1 starves.
    2271 To support this efficient semantics (and prevent barging), the implementation maintains a list of monitors acquired for each blocked thread.
    2272 When a signaller exits or waits in a monitor function/statement, the front waiter on urgent is unblocked if all its monitors are released.
    2273 Implementing a fast subset check for the necessary released monitors is important.
     2491To support these efficient semantics and prevent barging, the implementation maintains a list of monitors acquired for each blocked thread.
     2492When a signaller exits or waits in a mutex function or statement, the front waiter on urgent is unblocked if all its monitors are released.
     2493Implementing a fast subset check for the necessarily released monitors is important and discussed in the following sections.
    22742494% The benefit is encapsulating complexity into only two actions: passing monitors to the next owner when they should be released and conditionally waking threads if all conditions are met.
    22752495
    22762496
    2277 \subsection{Loose Object Definitions}
    2278 \label{s:LooseObjectDefinitions}
    2279 
    2280 In an object-oriented programming language, a class includes an exhaustive list of operations.
    2281 A new class can add members via static inheritance but the subclass still has an exhaustive list of operations.
    2282 (Dynamic member adding, \eg JavaScript~\cite{JavaScript}, is not considered.)
    2283 In the object-oriented scenario, the type and all its operators are always present at compilation (even separate compilation), so it is possible to number the operations in a bit mask and use an $O(1)$ compare with a similar bit mask created for the operations specified in a @waitfor@.
    2284 
    2285 However, in \CFA, monitor functions can be statically added/removed in translation units, making a fast subset check difficult.
    2286 \begin{cfa}
    2287         monitor M { ... }; // common type, included in .h file
    2288 translation unit 1
    2289         void `f`( M & mutex m );
    2290         void g( M & mutex m ) { waitfor( `f`, m ); }
    2291 translation unit 2
    2292         void `f`( M & mutex m ); $\C{// replacing f and g for type M in this translation unit}$
    2293         void `g`( M & mutex m );
    2294         void h( M & mutex m ) { waitfor( `f`, m ) or waitfor( `g`, m ); } $\C{// extending type M in this translation unit}$
    2295 \end{cfa}
    2296 The @waitfor@ statements in each translation unit cannot form a unique bit-mask because the monitor type does not carry that information.
     2497\subsection{\texorpdfstring{\protect\lstinline@waitfor@ Implementation}{waitfor Implementation}}
     2498\label{s:waitforImplementation}
     2499
     2500In a statically typed object-oriented programming language, a class has an exhaustive list of members, even when members are added via static inheritance (see Figure~\ref{f:uCinheritance}).
     2501Knowing all members at compilation, even separate compilation, allows uniquely numbered them so the accept-statement implementation can use a fast and compact bit mask with $O(1)$ compare.
     2502
     2503\begin{figure}
     2504\centering
     2505\begin{lrbox}{\myboxA}
     2506\begin{uC++}[aboveskip=0pt,belowskip=0pt]
     2507$\emph{translation unit 1}$
     2508_Monitor B { // common type in .h file
     2509        _Mutex virtual void `f`( ... );
     2510        _Mutex virtual void `g`( ... );
     2511        _Mutex virtual void w1( ... ) { ... _Accept(`f`, `g`); ... }
     2512};
     2513$\emph{translation unit 2}$
     2514// include B
     2515_Monitor D : public B { // inherit
     2516        _Mutex void `h`( ... ); // add
     2517        _Mutex void w2( ... ) { ... _Accept(`f`, `h`); ... }
     2518};
     2519\end{uC++}
     2520\end{lrbox}
     2521
     2522\begin{lrbox}{\myboxB}
     2523\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     2524$\emph{translation unit 1}$
     2525monitor M { ... }; // common type in .h file
     2526void `f`( M & mutex m, ... );
     2527void `g`( M & mutex m, ... );
     2528void w1( M & mutex m, ... ) { ... waitfor(`f`, `g` : m); ... }
     2529
     2530$\emph{translation unit 2}$
     2531// include M
     2532extern void `f`( M & mutex m, ... ); // import f but not g
     2533void `h`( M & mutex m ); // add
     2534void w2( M & mutex m, ... ) { ... waitfor(`f`, `h` : m); ... }
     2535
     2536\end{cfa}
     2537\end{lrbox}
     2538
     2539\subfloat[\uC]{\label{f:uCinheritance}\usebox\myboxA}
     2540\hspace{3pt}
     2541\vrule
     2542\hspace{3pt}
     2543\subfloat[\CFA]{\label{f:CFinheritance}\usebox\myboxB}
     2544\caption{Member / function visibility}
     2545\label{f:MemberFunctionVisibility}
     2546\end{figure}
     2547
     2548However, the @waitfor@ statement in translation unit 2 (see Figure~\ref{f:CFinheritance}) cannot see function @g@ in translation unit 1 precluding a unique numbering for a bit-mask because the monitor type only carries the protected shared data.
     2549(A possible way to construct a dense mapping is at link or load-time.)
    22972550Hence, function pointers are used to identify the functions listed in the @waitfor@ statement, stored in a variable-sized array.
    2298 Then, the same implementation approach used for the urgent stack is used for the calling queue.
    2299 Each caller has a list of monitors acquired, and the @waitfor@ statement performs a (usually short) linear search matching functions in the @waitfor@ list with called functions, and then verifying the associated mutex locks can be transfers.
    2300 (A possible way to construct a dense mapping is at link or load-time.)
    2301 
    2302 
    2303 \subsection{Multi-Monitor Scheduling}
     2551Then, the same implementation approach used for the urgent stack (see Section~\ref{s:Scheduling}) is used for the calling queue.
     2552Each caller has a list of monitors acquired, and the @waitfor@ statement performs a short linear search matching functions in the @waitfor@ list with called functions, and then verifying the associated mutex locks can be transferred.
     2553
     2554
     2555\subsection{Multimonitor scheduling}
    23042556\label{s:Multi-MonitorScheduling}
    23052557
    2306 External scheduling, like internal scheduling, becomes significantly more complex for multi-monitor semantics.
     2558External scheduling, like internal scheduling, becomes significantly more complex for multimonitor semantics.
    23072559Even in the simplest case, new semantics need to be established.
    23082560\begin{cfa}
     
    23132565The solution is for the programmer to disambiguate:
    23142566\begin{cfa}
    2315 waitfor( f, `m2` ); $\C{// wait for call to f with argument m2}$
     2567waitfor( f : `m2` ); $\C{// wait for call to f with argument m2}$
    23162568\end{cfa}
    23172569Both locks are acquired by function @g@, so when function @f@ is called, the lock for monitor @m2@ is passed from @g@ to @f@, while @g@ still holds lock @m1@.
    2318 This behaviour can be extended to the multi-monitor @waitfor@ statement.
     2570This behavior can be extended to the multimonitor @waitfor@ statement.
    23192571\begin{cfa}
    23202572monitor M { ... };
    23212573void f( M & mutex m1, M & mutex m2 );
    2322 void g( M & mutex m1, M & mutex m2 ) { waitfor( f, `m1, m2` ); $\C{// wait for call to f with arguments m1 and m2}$
     2574void g( M & mutex m1, M & mutex m2 ) { waitfor( f : `m1, m2` ); $\C{// wait for call to f with arguments m1 and m2}$
    23232575\end{cfa}
    23242576Again, the set of monitors passed to the @waitfor@ statement must be entirely contained in the set of monitors already acquired by the accepting function.
    2325 Also, the order of the monitors in a @waitfor@ statement is unimportant.
    2326 
    2327 Figure~\ref{f:UnmatchedMutexSets} shows an example where, for internal and external scheduling with multiple monitors, a signalling or accepting thread must match exactly, \ie partial matching results in waiting.
    2328 For both examples, the set of monitors is disjoint so unblocking is impossible.
     2577% Also, the order of the monitors in a @waitfor@ statement must match the order of the mutex parameters.
     2578
     2579Figure~\ref{f:UnmatchedMutexSets} shows internal and external scheduling with multiple monitors that must match exactly with a signaling or accepting thread, \ie partial matching results in waiting.
     2580In both cases, the set of monitors is disjoint so unblocking is impossible.
    23292581
    23302582\begin{figure}
     
    23552607}
    23562608void g( M1 & mutex m1, M2 & mutex m2 ) {
    2357         waitfor( f, m1, m2 );
     2609        waitfor( f : m1, m2 );
    23582610}
    23592611g( `m11`, m2 ); // block on accept
     
    23702622\end{figure}
    23712623
    2372 
    2373 \subsection{\texorpdfstring{\protect\lstinline@mutex@ Threads}{mutex Threads}}
    2374 
    2375 Threads in \CFA can also be monitors to allow \emph{direct communication} among threads, \ie threads can have mutex functions that are called by other threads.
    2376 Hence, all monitor features are available when using threads.
    2377 Figure~\ref{f:DirectCommunication} shows a comparison of direct call communication in \CFA with direct channel communication in Go.
    2378 (Ada provides a similar mechanism to the \CFA direct communication.)
    2379 The program main in both programs communicates directly with the other thread versus indirect communication where two threads interact through a passive monitor.
    2380 Both direct and indirection thread communication are valuable tools in structuring concurrent programs.
    2381 
    23822624\begin{figure}
    23832625\centering
     
    23862628
    23872629struct Msg { int i, j; };
    2388 thread GoRtn { int i;  float f;  Msg m; };
     2630mutex thread GoRtn { int i;  float f;  Msg m; };
    23892631void mem1( GoRtn & mutex gortn, int i ) { gortn.i = i; }
    23902632void mem2( GoRtn & mutex gortn, float f ) { gortn.f = f; }
     
    23922634void ^?{}( GoRtn & mutex ) {}
    23932635
    2394 void main( GoRtn & gortn ) with( gortn ) { // thread starts
     2636void main( GoRtn & mutex gortn ) with(gortn) { // thread starts
    23952637
    23962638        for () {
    23972639
    2398                 `waitfor( mem1, gortn )` sout | i;  // wait for calls
    2399                 or `waitfor( mem2, gortn )` sout | f;
    2400                 or `waitfor( mem3, gortn )` sout | m.i | m.j;
    2401                 or `waitfor( ^?{}, gortn )` break;
     2640                `waitfor( mem1 : gortn )` sout | i;  // wait for calls
     2641                or `waitfor( mem2 : gortn )` sout | f;
     2642                or `waitfor( mem3 : gortn )` sout | m.i | m.j;
     2643                or `waitfor( ^?{} : gortn )` break; // low priority
    24022644
    24032645        }
     
    24532695\hspace{3pt}
    24542696\subfloat[Go]{\label{f:Gochannel}\usebox\myboxB}
    2455 \caption{Direct communication}
    2456 \label{f:DirectCommunication}
     2697\caption{Direct versus indirect communication}
     2698\label{f:DirectCommunicationComparison}
     2699
     2700\medskip
     2701
     2702\begin{cfa}
     2703mutex thread DatingService {
     2704        condition Girls[CompCodes], Boys[CompCodes];
     2705        int girlPhoneNo, boyPhoneNo, ccode;
     2706};
     2707int girl( DatingService & mutex ds, int phoneno, int code ) with( ds ) {
     2708        girlPhoneNo = phoneno;  ccode = code;
     2709        `wait( Girls[ccode] );`                                                         $\C{// wait for boy}$
     2710        girlPhoneNo = phoneno;  return boyPhoneNo;
     2711}
     2712int boy( DatingService & mutex ds, int phoneno, int code ) with( ds ) {
     2713        boyPhoneNo = phoneno;  ccode = code;
     2714        `wait( Boys[ccode] );`                                                          $\C{// wait for girl}$
     2715        boyPhoneNo = phoneno;  return girlPhoneNo;
     2716}
     2717void main( DatingService & ds ) with( ds ) {                    $\C{// thread starts, ds defaults to mutex}$
     2718        for () {
     2719                waitfor( ^?{} ) break;                                                  $\C{// high priority}$
     2720                or waitfor( girl )                                                              $\C{// girl called, compatible boy ? restart boy then girl}$
     2721                        if ( ! is_empty( Boys[ccode] ) ) { `signal_block( Boys[ccode] );  signal_block( Girls[ccode] );` }
     2722                or waitfor( boy ) {                                                             $\C{// boy called, compatible girl ? restart girl then boy}$
     2723                        if ( ! is_empty( Girls[ccode] ) ) { `signal_block( Girls[ccode] );  signal_block( Boys[ccode] );` }
     2724        }
     2725}
     2726\end{cfa}
     2727\caption{Direct communication dating service}
     2728\label{f:DirectCommunicationDatingService}
    24572729\end{figure}
    24582730
     
    24692741void main( Ping & pi ) {
    24702742        for ( 10 ) {
    2471                 `waitfor( ping, pi );`
     2743                `waitfor( ping : pi );`
    24722744                `pong( po );`
    24732745        }
     
    24822754        for ( 10 ) {
    24832755                `ping( pi );`
    2484                 `waitfor( pong, po );`
     2756                `waitfor( pong : po );`
    24852757        }
    24862758}
     
    24932765% \label{f:pingpong}
    24942766% \end{figure}
    2495 Note, the ping/pong threads are globally declared, @pi@/@po@, and hence, start (and possibly complete) before the program main starts.
     2767Note, the ping/pong threads are globally declared, @pi@/@po@, and hence, start and possibly complete before the program main starts.
    24962768\end{comment}
    24972769
    24982770
    2499 \subsection{Execution Properties}
    2500 
    2501 Table~\ref{t:ObjectPropertyComposition} shows how the \CFA high-level constructs cover 3 fundamental execution properties: thread, stateful function, and mutual exclusion.
    2502 Case 1 is a basic object, with none of the new execution properties.
    2503 Case 2 allows @mutex@ calls to Case 1 to protect shared data.
    2504 Case 3 allows stateful functions to suspend/resume but restricts operations because the state is stackless.
    2505 Case 4 allows @mutex@ calls to Case 3 to protect shared data.
    2506 Cases 5 and 6 are the same as 3 and 4 without restriction because the state is stackful.
    2507 Cases 7 and 8 are rejected because a thread cannot execute without a stackful state in a preemptive environment when context switching from the signal handler.
    2508 Cases 9 and 10 have a stackful thread without and with @mutex@ calls.
    2509 For situations where threads do not require direct communication, case 9 provides faster creation/destruction by eliminating @mutex@ setup.
    2510 
    2511 \begin{table}
    2512 \caption{Object property composition}
    2513 \centering
    2514 \label{t:ObjectPropertyComposition}
    2515 \renewcommand{\arraystretch}{1.25}
    2516 %\setlength{\tabcolsep}{5pt}
    2517 \begin{tabular}{c|c||l|l}
    2518 \multicolumn{2}{c||}{object properties} & \multicolumn{2}{c}{mutual exclusion} \\
    2519 \hline
    2520 thread  & stateful                              & \multicolumn{1}{c|}{No} & \multicolumn{1}{c}{Yes} \\
    2521 \hline
    2522 \hline
    2523 No              & No                                    & \textbf{1}\ \ \ aggregate type                & \textbf{2}\ \ \ @monitor@ aggregate type \\
    2524 \hline
    2525 No              & Yes (stackless)               & \textbf{3}\ \ \ @generator@                   & \textbf{4}\ \ \ @monitor@ @generator@ \\
    2526 \hline
    2527 No              & Yes (stackful)                & \textbf{5}\ \ \ @coroutine@                   & \textbf{6}\ \ \ @monitor@ @coroutine@ \\
    2528 \hline
    2529 Yes             & No / Yes (stackless)  & \textbf{7}\ \ \ {\color{red}rejected} & \textbf{8}\ \ \ {\color{red}rejected} \\
    2530 \hline
    2531 Yes             & Yes (stackful)                & \textbf{9}\ \ \ @thread@                              & \textbf{10}\ \ @monitor@ @thread@ \\
    2532 \end{tabular}
    2533 \end{table}
     2771\subsection{\texorpdfstring{\protect\lstinline@mutex@ Generators / coroutines / threads}{monitor Generators / coroutines / threads}}
     2772
     2773\CFA generators, coroutines, and threads can also be @mutex@ (Table~\ref{t:ExecutionPropertyComposition} cases 4, 6, 12) allowing safe \emph{direct communication} with threads, \ie the custom types can have mutex functions that are called by other threads.
     2774All monitor features are available within these mutex functions.
     2775For example, if the formatter generator or coroutine equivalent in Figure~\ref{f:CFAFormatGen} is extended with the monitor property and this interface function is used to communicate with the formatter:
     2776\begin{cfa}
     2777void fmt( Fmt & mutex fmt, char ch ) { fmt.ch = ch; resume( fmt ) }
     2778\end{cfa}
     2779multiple threads can safely pass characters for formatting.
     2780
     2781Figure~\ref{f:DirectCommunicationComparison} shows a comparison of direct call-communication in \CFA versus indirect channel-communication in Go.
     2782(Ada has a similar mechanism to \CFA direct communication.)
     2783% The thread main function is by default @mutex@, so the @mutex@ qualifier for the thread parameter is optional.
     2784% The reason is that the thread logically starts instantaneously in the thread main acquiring its mutual exclusion, so it starts before any calls to prepare for synchronizing these calls.
     2785The \CFA program @main@ uses the call/return paradigm to directly communicate with the @GoRtn main@, whereas Go switches to the unbuffered channel paradigm to indirectly communicate with the goroutine.
     2786Communication by multiple threads is safe for the @gortn@ thread via mutex calls in \CFA or channel assignment in Go.
     2787The difference between call and channel send occurs for buffered channels making the send asynchronous.
     2788In \CFA, asynchronous call and multiple buffers are provided using an administrator and worker threads~\cite{Gentleman81} and/or futures (not discussed).
     2789
     2790Figure~\ref{f:DirectCommunicationDatingService} shows the dating-service problem in Figure~\ref{f:DatingServiceMonitor} extended from indirect monitor communication to direct thread communication.
     2791When converting a monitor to a thread (server), the coding pattern is to move as much code as possible from the accepted functions into the thread main so it does as much work as possible.
     2792Notice, the dating server is postponing requests for an unspecified time while continuing to accept new requests.
     2793For complex servers, \eg web-servers, there can be hundreds of lines of code in the thread main and safe interaction with clients can be complex.
    25342794
    25352795
     
    25372797
    25382798For completeness and efficiency, \CFA provides a standard set of low-level locks: recursive mutex, condition, semaphore, barrier, \etc, and atomic instructions: @fetchAssign@, @fetchAdd@, @testSet@, @compareSet@, \etc.
    2539 Some of these low-level mechanism are used in the \CFA runtime, but we strongly advocate using high-level mechanisms whenever possible.
     2799Some of these low-level mechanisms are used to build the \CFA runtime, but we always advocate using high-level mechanisms whenever possible.
    25402800
    25412801
     
    25512811%
    25522812%
    2553 % \subsection{User Threads}
     2813% \subsection{User threads}
    25542814%
    25552815% A direct improvement on kernel threads is user threads, \eg Erlang~\cite{Erlang} and \uC~\cite{uC++book}.
     
    25662826
    25672827\begin{comment}
    2568 \subsection{Thread Pools}
     2828\subsection{Thread pools}
    25692829
    25702830In contrast to direct threading is indirect \newterm{thread pools}, \eg Java @executor@, where small jobs (work units) are inserted into a work pool for execution.
    2571 If the jobs are dependent, \ie interact, there is an implicit/explicit dependency graph that ties them together.
     2831If the jobs are dependent, \ie interact, there is an implicit dependency graph that ties them together.
    25722832While removing direct concurrency, and hence the amount of context switching, thread pools significantly limit the interaction that can occur among jobs.
    25732833Indeed, jobs should not block because that also blocks the underlying thread, which effectively means the CPU utilization, and therefore throughput, suffers.
     
    25802840\begin{cfa}
    25812841struct Adder {
    2582     int * row, cols;
     2842        int * row, cols;
    25832843};
    25842844int operator()() {
     
    26392899\label{s:RuntimeStructureCluster}
    26402900
    2641 A \newterm{cluster} is a collection of threads and virtual processors (abstract kernel-thread) that execute the (user) threads from its own ready queue (like an OS executing kernel threads).
     2901A \newterm{cluster} is a collection of user and kernel threads, where the kernel threads run the user threads from the cluster's ready queue, and the operating system runs the kernel threads on the processors from its ready queue~\cite{Buhr90a}.
     2902The term \newterm{virtual processor} is introduced as a synonym for kernel thread to disambiguate between user and kernel thread.
     2903From the language perspective, a virtual processor is an actual processor (core).
     2904
    26422905The purpose of a cluster is to control the amount of parallelism that is possible among threads, plus scheduling and other execution defaults.
    26432906The default cluster-scheduler is single-queue multi-server, which provides automatic load-balancing of threads on processors.
    2644 However, the design allows changing the scheduler, \eg multi-queue multi-server with work-stealing/sharing across the virtual processors.
     2907However, the design allows changing the scheduler, \eg multi-queue multiserver with work-stealing/sharing across the virtual processors.
    26452908If several clusters exist, both threads and virtual processors, can be explicitly migrated from one cluster to another.
    26462909No automatic load balancing among clusters is performed by \CFA.
     
    26522915
    26532916
    2654 \subsection{Virtual Processor}
     2917\subsection{Virtual processor}
    26552918\label{s:RuntimeStructureProcessor}
    26562919
    2657 A virtual processor is implemented by a kernel thread (\eg UNIX process), which are scheduled for execution on a hardware processor by the underlying operating system.
     2920A virtual processor is implemented by a kernel thread, \eg UNIX process, which are scheduled for execution on a hardware processor by the underlying operating system.
    26582921Programs may use more virtual processors than hardware processors.
    26592922On a multiprocessor, kernel threads are distributed across the hardware processors resulting in virtual processors executing in parallel.
    2660 (It is possible to use affinity to lock a virtual processor onto a particular hardware processor~\cite{affinityLinux, affinityWindows, affinityFreebsd, affinityNetbsd, affinityMacosx}, which is used when caching issues occur or for heterogeneous hardware processors.)
     2923(It is possible to use affinity to lock a virtual processor onto a particular hardware processor~\cite{affinityLinux,affinityWindows}, which is used when caching issues occur or for heterogeneous hardware processors.) %, affinityFreebsd, affinityNetbsd, affinityMacosx
    26612924The \CFA runtime attempts to block unused processors and unblock processors as the system load increases;
    2662 balancing the workload with processors is difficult because it requires future knowledge, \ie what will the applicaton workload do next.
     2925balancing the workload with processors is difficult because it requires future knowledge, \ie what will the application workload do next.
    26632926Preemption occurs on virtual processors rather than user threads, via operating-system interrupts.
    26642927Thus virtual processors execute user threads, where preemption frequency applies to a virtual processor, so preemption occurs randomly across the executed user threads.
     
    26702933\label{s:Implementation}
    26712934
    2672 A primary implementation challenge is avoiding contention from dynamically allocating memory because of bulk acquire, \eg the internal-scheduling design is (almost) free of allocations.
     2935A primary implementation challenge is avoiding contention from dynamically allocating memory because of bulk acquire, \eg the internal-scheduling design is almost free of allocations.
    26732936All blocking operations are made by parking threads onto queues, therefore all queues are designed with intrusive nodes, where each node has preallocated link fields for chaining.
    26742937Furthermore, several bulk-acquire operations need a variable amount of memory.
    26752938This storage is allocated at the base of a thread's stack before blocking, which means programmers must add a small amount of extra space for stacks.
    26762939
    2677 In \CFA, ordering of monitor acquisition relies on memory ordering to prevent deadlock~\cite{Havender68}, because all objects have distinct non-overlapping memory layouts, and mutual-exclusion for a monitor is only defined for its lifetime.
     2940In \CFA, ordering of monitor acquisition relies on memory ordering to prevent deadlock~\cite{Havender68}, because all objects have distinct nonoverlapping memory layouts, and mutual-exclusion for a monitor is only defined for its lifetime.
    26782941When a mutex call is made, pointers to the concerned monitors are aggregated into a variable-length array and sorted.
    26792942This array persists for the entire duration of the mutual exclusion and is used extensively for synchronization operations.
     
    26942957
    26952958Nondeterministic preemption provides fairness from long-running threads, and forces concurrent programmers to write more robust programs, rather than relying on code between cooperative scheduling to be atomic.
    2696 This atomic reliance can fail on multi-core machines, because execution across cores is nondeterministic.
    2697 A different reason for not supporting preemption is that it significantly complicates the runtime system, \eg Microsoft runtime does not support interrupts and on Linux systems, interrupts are complex (see below).
     2959This atomic reliance can fail on multicore machines, because execution across cores is nondeterministic.
     2960A different reason for not supporting preemption is that it significantly complicates the runtime system, \eg Windows runtime does not support interrupts and on Linux systems, interrupts are complex (see below).
    26982961Preemption is normally handled by setting a countdown timer on each virtual processor.
    2699 When the timer expires, an interrupt is delivered, and the interrupt handler resets the countdown timer, and if the virtual processor is executing in user code, the signal handler performs a user-level context-switch, or if executing in the language runtime kernel, the preemption is ignored or rolled forward to the point where the runtime kernel context switches back to user code.
     2962When the timer expires, an interrupt is delivered, and its signal handler resets the countdown timer, and if the virtual processor is executing in user code, the signal handler performs a user-level context-switch, or if executing in the language runtime kernel, the preemption is ignored or rolled forward to the point where the runtime kernel context switches back to user code.
    27002963Multiple signal handlers may be pending.
    27012964When control eventually switches back to the signal handler, it returns normally, and execution continues in the interrupted user thread, even though the return from the signal handler may be on a different kernel thread than the one where the signal is delivered.
    27022965The only issue with this approach is that signal masks from one kernel thread may be restored on another as part of returning from the signal handler;
    27032966therefore, the same signal mask is required for all virtual processors in a cluster.
    2704 Because preemption frequency is usually long (1 millisecond) performance cost is negligible.
    2705 
    2706 Linux switched a decade ago from specific to arbitrary process signal-delivery for applications with multiple kernel threads.
    2707 \begin{cquote}
    2708 A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked.
    2709 If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which it will deliver the signal.
    2710 SIGNAL(7) - Linux Programmer's Manual
    2711 \end{cquote}
     2967Because preemption interval is usually long (1 ms) performance cost is negligible.
     2968
     2969Linux switched a decade ago from specific to arbitrary virtual-processor signal-delivery for applications with multiple kernel threads.
     2970In the new semantics, a virtual-processor directed signal may be delivered to any virtual processor created by the application that does not have the signal blocked.
    27122971Hence, the timer-expiry signal, which is generated \emph{externally} by the Linux kernel to an application, is delivered to any of its Linux subprocesses (kernel threads).
    27132972To ensure each virtual processor receives a preemption signal, a discrete-event simulation is run on a special virtual processor, and only it sets and receives timer events.
     
    27172976
    27182977
    2719 \subsection{Debug Kernel}
    2720 
    2721 There are two versions of the \CFA runtime kernel: debug and non-debug.
    2722 The debugging version has many runtime checks and internal assertions, \eg stack (non-writable) guard page, and checks for stack overflow whenever context switches occur among coroutines and threads, which catches most stack overflows.
    2723 After a program is debugged, the non-debugging version can be used to significantly decrease space and increase performance.
     2978\subsection{Debug kernel}
     2979
     2980There are two versions of the \CFA runtime kernel: debug and nondebug.
     2981The debugging version has many runtime checks and internal assertions, \eg stack nonwritable guard page, and checks for stack overflow whenever context switches occur among coroutines and threads, which catches most stack overflows.
     2982After a program is debugged, the nondebugging version can be used to significantly decrease space and increase performance.
    27242983
    27252984
     
    27272986\label{s:Performance}
    27282987
    2729 To verify the implementation of the \CFA runtime, a series of microbenchmarks are performed comparing \CFA with pthreads, Java OpenJDK-9, Go 1.12.6 and \uC 7.0.0.
    2730 For comparison, the package must be multi-processor (M:N), which excludes libdill/libmil~\cite{libdill} (M:1)), and use a shared-memory programming model, \eg not message passing.
    2731 The benchmark computer is an AMD Opteron\texttrademark\ 6380 NUMA 64-core, 8 socket, 2.5 GHz processor, running Ubuntu 16.04.6 LTS, and \CFA/\uC are compiled with gcc 6.5.
    2732 
    2733 All benchmarks are run using the following harness. (The Java harness is augmented to circumvent JIT issues.)
    2734 \begin{cfa}
    2735 unsigned int N = 10_000_000;
    2736 #define BENCH( `run` ) Time before = getTimeNsec();  `run;`  Duration result = (getTimeNsec() - before) / N;
    2737 \end{cfa}
    2738 The method used to get time is @clock_gettime( CLOCK_REALTIME )@.
    2739 Each benchmark is performed @N@ times, where @N@ varies depending on the benchmark;
    2740 the total time is divided by @N@ to obtain the average time for a benchmark.
    2741 Each benchmark experiment is run 31 times.
    2742 All omitted tests for other languages are functionally identical to the \CFA tests and available online~\cite{CforallBenchMarks}.
    2743 % tar --exclude=.deps --exclude=Makefile --exclude=Makefile.in --exclude=c.c --exclude=cxx.cpp --exclude=fetch_add.c -cvhf benchmark.tar benchmark
    2744 
    2745 \paragraph{Object Creation}
    2746 
    2747 Object creation is measured by creating/deleting the specific kind of concurrent object.
    2748 Figure~\ref{f:creation} shows the code for \CFA, with results in Table~\ref{tab:creation}.
    2749 The only note here is that the call stacks of \CFA coroutines are lazily created, therefore without priming the coroutine to force stack creation, the creation cost is artificially low.
     2988To test the performance of the \CFA runtime, a series of microbenchmarks are used to compare \CFA with pthreads, Java 11.0.6, Go 1.12.6, Rust 1.37.0, Python 3.7.6, Node.js 12.14.1, and \uC 7.0.0.
     2989For comparison, the package must be multiprocessor (M:N), which excludes libdil and libmil~\cite{libdill} (M:1)), and use a shared-memory programming model, \eg not message passing.
     2990The benchmark computer is an AMD Opteron\texttrademark\ 6380 NUMA 64-core, 8 socket, 2.5 GHz processor, running Ubuntu 16.04.6 LTS, and pthreads/\CFA/\uC are compiled with gcc 9.2.1.
     2991
     2992All benchmarks are run using the following harness.
     2993(The Java harness is augmented to circumvent JIT issues.)
     2994\begin{cfa}
     2995#define BENCH( `run` ) uint64_t start = cputime_ns();  `run;`  double result = (double)(cputime_ns() - start) / N;
     2996\end{cfa}
     2997where CPU time in nanoseconds is from the appropriate language clock.
     2998Each benchmark is performed @N@ times, where @N@ is selected so the benchmark runs in the range of 2--20 s for the specific programming language;
     2999each @N@ appears after the experiment name in the following tables.
     3000The total time is divided by @N@ to obtain the average time for a benchmark.
     3001Each benchmark experiment is run 13 times and the average appears in the table.
     3002For languages with a runtime JIT (Java, Node.js, Python), a single half-hour long experiment is run to check stability;
     3003all long-experiment results are statistically equivalent, \ie median/average/SD correlate with the short-experiment results, indicating the short experiments reached a steady state.
     3004All omitted tests for other languages are functionally identical to the \CFA tests and available online~\cite{CforallConcurrentBenchmarks}.
     3005
     3006\subsection{Creation}
     3007
     3008Creation is measured by creating and deleting a specific kind of control-flow object.
     3009Figure~\ref{f:creation} shows the code for \CFA with results in Table~\ref{t:creation}.
     3010Note, the call stacks of \CFA coroutines are lazily created on the first resume, therefore the cost of creation with and without a stack are presented.
    27503011
    27513012\begin{multicols}{2}
    2752 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}}
    2753 \begin{cfa}
    2754 @thread@ MyThread {};
    2755 void @main@( MyThread & ) {}
     3013\begin{cfa}[xleftmargin=0pt]
     3014`coroutine` MyCoroutine {};
     3015void ?{}( MyCoroutine & this ) {
     3016#ifdef EAGER
     3017        resume( this );
     3018#endif
     3019}
     3020void main( MyCoroutine & ) {}
    27563021int main() {
    2757         BENCH( for ( N ) { @MyThread m;@ } )
    2758         sout | result`ns;
    2759 }
    2760 \end{cfa}
    2761 \captionof{figure}{\CFA object-creation benchmark}
     3022        BENCH( for ( N ) { `MyCoroutine c;` } )
     3023        sout | result;
     3024}
     3025\end{cfa}
     3026\captionof{figure}{\CFA creation benchmark}
    27623027\label{f:creation}
    27633028
     
    27653030
    27663031\vspace*{-16pt}
    2767 \captionof{table}{Object creation comparison (nanoseconds)}
    2768 \label{tab:creation}
     3032\captionof{table}{Creation comparison (nanoseconds)}
     3033\label{t:creation}
    27693034
    27703035\begin{tabular}[t]{@{}r*{3}{D{.}{.}{5.2}}@{}}
    2771 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
    2772 \CFA Coroutine Lazy             & 13.2          & 13.1          & 0.44          \\
    2773 \CFA Coroutine Eager    & 531.3         & 536.0         & 26.54         \\
    2774 \CFA Thread                             & 2074.9        & 2066.5        & 170.76        \\
    2775 \uC Coroutine                   & 89.6          & 90.5          & 1.83          \\
    2776 \uC Thread                              & 528.2         & 528.5         & 4.94          \\
    2777 Goroutine                               & 4068.0        & 4113.1        & 414.55        \\
    2778 Java Thread                             & 103848.5      & 104295.4      & 2637.57       \\
    2779 Pthreads                                & 33112.6       & 33127.1       & 165.90
     3036\multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
     3037\CFA generator (1B)                     & 0.6           & 0.6           & 0.0           \\
     3038\CFA coroutine lazy     (100M)  & 13.4          & 13.1          & 0.5           \\
     3039\CFA coroutine eager (10M)      & 144.7         & 143.9         & 1.5           \\
     3040\CFA thread (10M)                       & 466.4         & 468.0         & 11.3          \\
     3041\uC coroutine (10M)                     & 155.6         & 155.7         & 1.7           \\
     3042\uC thread (10M)                        & 523.4         & 523.9         & 7.7           \\
     3043Python generator (10M)          & 123.2         & 124.3         & 4.1           \\
     3044Node.js generator (10M)         & 33.4          & 33.5          & 0.3           \\
     3045Goroutine thread (10M)          & 751.0         & 750.5         & 3.1           \\
     3046Rust tokio thread (10M)         & 1860.0        & 1881.1        & 37.6          \\
     3047Rust thread     (250K)                  & 53801.0       & 53896.8       & 274.9         \\
     3048Java thread (250K)                      & 119256.0      & 119679.2      & 2244.0        \\
     3049% Java thread (1 000 000)               & 123100.0      & 123052.5      & 751.6         \\
     3050Pthreads thread (250K)          & 31465.5       & 31419.5       & 140.4
    27803051\end{tabular}
    27813052\end{multicols}
    27823053
    2783 
    2784 \paragraph{Context-Switching}
     3054\vspace*{-10pt}
     3055\subsection{Internal scheduling}
     3056
     3057Internal scheduling is measured using a cycle of two threads signaling and waiting.
     3058Figure~\ref{f:schedint} shows the code for \CFA, with results in Table~\ref{t:schedint}.
     3059Note, the \CFA incremental cost for bulk acquire is a fixed cost for small numbers of mutex objects.
     3060User-level threading has one kernel thread, eliminating contention between the threads (direct handoff of the kernel thread).
     3061Kernel-level threading has two kernel threads allowing some contention.
     3062
     3063\begin{multicols}{2}
     3064\setlength{\tabcolsep}{3pt}
     3065\begin{cfa}[xleftmargin=0pt]
     3066volatile int go = 0;
     3067`condition c;`
     3068`monitor` M {} m1/*, m2, m3, m4*/;
     3069void call( M & `mutex p1/*, p2, p3, p4*/` ) {
     3070        `signal( c );`
     3071}
     3072void wait( M & `mutex p1/*, p2, p3, p4*/` ) {
     3073        go = 1; // continue other thread
     3074        for ( N ) { `wait( c );` } );
     3075}
     3076thread T {};
     3077void main( T & ) {
     3078        while ( go == 0 ) { yield(); } // waiter must start first
     3079        BENCH( for ( N ) { call( m1/*, m2, m3, m4*/ ); } )
     3080        sout | result;
     3081}
     3082int main() {
     3083        T t;
     3084        wait( m1/*, m2, m3, m4*/ );
     3085}
     3086\end{cfa}
     3087\vspace*{-8pt}
     3088\captionof{figure}{\CFA Internal-scheduling benchmark}
     3089\label{f:schedint}
     3090
     3091\columnbreak
     3092
     3093\vspace*{-16pt}
     3094\captionof{table}{Internal-scheduling comparison (nanoseconds)}
     3095\label{t:schedint}
     3096\bigskip
     3097
     3098\begin{tabular}{@{}r*{3}{D{.}{.}{5.2}}@{}}
     3099\multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
     3100\CFA @signal@, 1 monitor (10M)  & 364.4         & 364.2         & 4.4           \\
     3101\CFA @signal@, 2 monitor (10M)  & 484.4         & 483.9         & 8.8           \\
     3102\CFA @signal@, 4 monitor (10M)  & 709.1         & 707.7         & 15.0          \\
     3103\uC @signal@ monitor (10M)              & 328.3         & 327.4         & 2.4           \\
     3104Rust cond. variable     (1M)            & 7514.0        & 7437.4        & 397.2         \\
     3105Java @notify@ monitor (1M)              & 8717.0        & 8774.1        & 471.8         \\
     3106% Java @notify@ monitor (100 000 000)           & 8634.0        & 8683.5        & 330.5         \\
     3107Pthreads cond. variable (1M)    & 5553.7        & 5576.1        & 345.6
     3108\end{tabular}
     3109\end{multicols}
     3110
     3111
     3112\subsection{External scheduling}
     3113
     3114External scheduling is measured using a cycle of two threads calling and accepting the call using the @waitfor@ statement.
     3115Figure~\ref{f:schedext} shows the code for \CFA with results in Table~\ref{t:schedext}.
     3116Note, the \CFA incremental cost for bulk acquire is a fixed cost for small numbers of mutex objects.
     3117
     3118\begin{multicols}{2}
     3119\setlength{\tabcolsep}{5pt}
     3120\vspace*{-16pt}
     3121\begin{cfa}[xleftmargin=0pt]
     3122`monitor` M {} m1/*, m2, m3, m4*/;
     3123void call( M & `mutex p1/*, p2, p3, p4*/` ) {}
     3124void wait( M & `mutex p1/*, p2, p3, p4*/` ) {
     3125        for ( N ) { `waitfor( call : p1/*, p2, p3, p4*/ );` }
     3126}
     3127thread T {};
     3128void main( T & ) {
     3129        BENCH( for ( N ) { call( m1/*, m2, m3, m4*/ ); } )
     3130        sout | result;
     3131}
     3132int main() {
     3133        T t;
     3134        wait( m1/*, m2, m3, m4*/ );
     3135}
     3136\end{cfa}
     3137\captionof{figure}{\CFA external-scheduling benchmark}
     3138\label{f:schedext}
     3139
     3140\columnbreak
     3141
     3142\vspace*{-18pt}
     3143\captionof{table}{External-scheduling comparison (nanoseconds)}
     3144\label{t:schedext}
     3145\begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}}
     3146\multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
     3147\CFA @waitfor@, 1 monitor (10M) & 367.1 & 365.3 & 5.0   \\
     3148\CFA @waitfor@, 2 monitor (10M) & 463.0 & 464.6 & 7.1   \\
     3149\CFA @waitfor@, 4 monitor (10M) & 689.6 & 696.2 & 21.5  \\
     3150\uC \lstinline[language=uC++]|_Accept| monitor (10M)    & 328.2 & 329.1 & 3.4   \\
     3151Go \lstinline[language=Golang]|select| channel (10M)    & 365.0 & 365.5 & 1.2
     3152\end{tabular}
     3153\end{multicols}
     3154
     3155\subsection{Mutual-Exclusion}
     3156
     3157Uncontented mutual exclusion, which frequently occurs, is measured by entering and leaving a critical section.
     3158For monitors, entering and leaving a mutex function are measured, otherwise the language-appropriate mutex-lock is measured.
     3159For comparison, a spinning (vs.\ blocking) test-and-test-set lock is presented.
     3160Figure~\ref{f:mutex} shows the code for \CFA with results in Table~\ref{t:mutex}.
     3161Note the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects.
     3162
     3163\begin{multicols}{2}
     3164\setlength{\tabcolsep}{3pt}
     3165\begin{cfa}[xleftmargin=0pt]
     3166`monitor` M {} m1/*, m2, m3, m4*/;
     3167call( M & `mutex p1/*, p2, p3, p4*/` ) {}
     3168int main() {
     3169        BENCH( for( N ) call( m1/*, m2, m3, m4*/ ); )
     3170        sout | result;
     3171}
     3172\end{cfa}
     3173\captionof{figure}{\CFA acquire/release mutex benchmark}
     3174\label{f:mutex}
     3175
     3176\columnbreak
     3177
     3178\vspace*{-16pt}
     3179\captionof{table}{Mutex comparison (nanoseconds)}
     3180\label{t:mutex}
     3181\begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}}
     3182\multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
     3183test-and-test-set lock (50M)            & 19.1  & 18.9  & 0.4   \\
     3184\CFA @mutex@ function, 1 arg. (50M)     & 48.3  & 47.8  & 0.9   \\
     3185\CFA @mutex@ function, 2 arg. (50M)     & 86.7  & 87.6  & 1.9   \\
     3186\CFA @mutex@ function, 4 arg. (50M)     & 173.4 & 169.4 & 5.9   \\
     3187\uC @monitor@ member rtn. (50M)         & 54.8  & 54.8  & 0.1   \\
     3188Goroutine mutex lock (50M)                      & 34.0  & 34.0  & 0.0   \\
     3189Rust mutex lock (50M)                           & 33.0  & 33.2  & 0.8   \\
     3190Java synchronized method (50M)          & 31.0  & 30.9  & 0.5   \\
     3191% Java synchronized method (10 000 000 000)             & 31.0 & 30.2 & 0.9 \\
     3192Pthreads mutex Lock (50M)                       & 31.0  & 31.1  & 0.4
     3193\end{tabular}
     3194\end{multicols}
     3195
     3196\subsection{Context switching}
    27853197
    27863198In procedural programming, the cost of a function call is important as modularization (refactoring) increases.
    2787 (In many cases, a compiler inlines function calls to eliminate this cost.)
    2788 Similarly, when modularization extends to coroutines/tasks, the time for a context switch becomes a relevant factor.
     3199(In many cases, a compiler inlines function calls to increase the size and number of basic blocks for optimizing.)
     3200Similarly, when modularization extends to coroutines and threads, the time for a context switch becomes a relevant factor.
    27893201The coroutine test is from resumer to suspender and from suspender to resumer, which is two context switches.
     3202%For async-await systems, the test is scheduling and fulfilling @N@ empty promises, where all promises are allocated before versus interleaved with fulfillment to avoid garbage collection.
     3203For async-await systems, the test measures the cost of the @await@ expression entering the event engine by awaiting @N@ promises, where each created promise is resolved by an immediate event in the engine (using Node.js @setImmediate@).
    27903204The thread test is using yield to enter and return from the runtime kernel, which is two context switches.
    27913205The difference in performance between coroutine and thread context-switch is the cost of scheduling for threads, whereas coroutines are self-scheduling.
    2792 Figure~\ref{f:ctx-switch} only shows the \CFA code for coroutines/threads (other systems are similar) with all results in Table~\ref{tab:ctx-switch}.
     3206Figure~\ref{f:ctx-switch} shows the \CFA code for a coroutine and thread with results in Table~\ref{t:ctx-switch}.
     3207
     3208% From: Gregor Richards <gregor.richards@uwaterloo.ca>
     3209% To: "Peter A. Buhr" <pabuhr@plg2.cs.uwaterloo.ca>
     3210% Date: Fri, 24 Jan 2020 13:49:18 -0500
     3211%
     3212% I can also verify that the previous version, which just tied a bunch of promises together, *does not* go back to the
     3213% event loop at all in the current version of Node. Presumably they're taking advantage of the fact that the ordering of
     3214% events is intentionally undefined to just jump right to the next 'then' in the chain, bypassing event queueing
     3215% entirely. That's perfectly correct behavior insofar as its difference from the specified behavior isn't observable, but
     3216% it isn't typical or representative of much anything useful, because most programs wouldn't have whole chains of eager
     3217% promises. Also, it's not representative of *anything* you can do with async/await, as there's no way to encode such an
     3218% eager chain that way.
    27933219
    27943220\begin{multicols}{2}
    2795 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}}
    2796 \begin{cfa}[aboveskip=0pt,belowskip=0pt]
    2797 @coroutine@ C {} c;
    2798 void main( C & ) { for ( ;; ) { @suspend;@ } }
     3221\begin{cfa}[xleftmargin=0pt]
     3222`coroutine` C {};
     3223void main( C & ) { for () { `suspend;` } }
    27993224int main() { // coroutine test
    2800         BENCH( for ( N ) { @resume( c );@ } )
    2801         sout | result`ns;
    2802 }
    2803 int main() { // task test
    2804         BENCH( for ( N ) { @yield();@ } )
    2805         sout | result`ns;
     3225        C c;
     3226        BENCH( for ( N ) { `resume( c );` } )
     3227        sout | result;
     3228}
     3229int main() { // thread test
     3230        BENCH( for ( N ) { `yield();` } )
     3231        sout | result;
    28063232}
    28073233\end{cfa}
     
    28133239\vspace*{-16pt}
    28143240\captionof{table}{Context switch comparison (nanoseconds)}
    2815 \label{tab:ctx-switch}
     3241\label{t:ctx-switch}
    28163242\begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}}
    2817 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
    2818 C function              & 1.8   & 1.8   & 0.01  \\
    2819 \CFA generator  & 2.4   & 2.2   & 0.25  \\
    2820 \CFA Coroutine  & 36.2  & 36.2  & 0.25  \\
    2821 \CFA Thread             & 93.2  & 93.5  & 2.09  \\
    2822 \uC Coroutine   & 52.0  & 52.1  & 0.51  \\
    2823 \uC Thread              & 96.2  & 96.3  & 0.58  \\
    2824 Goroutine               & 141.0 & 141.3 & 3.39  \\
    2825 Java Thread             & 374.0 & 375.8 & 10.38 \\
    2826 Pthreads Thread & 361.0 & 365.3 & 13.19
     3243\multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
     3244C function (10B)                        & 1.8           & 1.8           & 0.0   \\
     3245\CFA generator (5B)                     & 1.8           & 2.0           & 0.3   \\
     3246\CFA coroutine (100M)           & 32.5          & 32.9          & 0.8   \\
     3247\CFA thread (100M)                      & 93.8          & 93.6          & 2.2   \\
     3248\uC coroutine (100M)            & 50.3          & 50.3          & 0.2   \\
     3249\uC thread (100M)                       & 97.3          & 97.4          & 1.0   \\
     3250Python generator (100M)         & 40.9          & 41.3          & 1.5   \\
     3251Node.js await (5M)                      & 1852.2        & 1854.7        & 16.4  \\
     3252Node.js generator (100M)        & 33.3          & 33.4          & 0.3   \\
     3253Goroutine thread (100M)         & 143.0         & 143.3         & 1.1   \\
     3254Rust async await (100M)         & 32.0          & 32.0          & 0.0   \\
     3255Rust tokio thread (100M)        & 143.0         & 143.0         & 1.7   \\
     3256Rust thread (25M)                       & 332.0         & 331.4         & 2.4   \\
     3257Java thread (100M)                      & 405.0         & 415.0         & 17.6  \\
     3258% Java thread (  100 000 000)                   & 413.0 & 414.2 & 6.2 \\
     3259% Java thread (5 000 000 000)                   & 415.0 & 415.2 & 6.1 \\
     3260Pthreads thread (25M)           & 334.3         & 335.2         & 3.9
    28273261\end{tabular}
    28283262\end{multicols}
    28293263
    28303264
    2831 \paragraph{Mutual-Exclusion}
    2832 
    2833 Uncontented mutual exclusion, which frequently occurs, is measured by entering/leaving a critical section.
    2834 For monitors, entering and leaving a monitor function is measured.
    2835 To put the results in context, the cost of entering a non-inline function and the cost of acquiring and releasing a @pthread_mutex@ lock is also measured.
    2836 Figure~\ref{f:mutex} shows the code for \CFA with all results in Table~\ref{tab:mutex}.
    2837 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects.
    2838 
    2839 \begin{multicols}{2}
    2840 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}}
    2841 \begin{cfa}
    2842 @monitor@ M {} m1/*, m2, m3, m4*/;
    2843 void __attribute__((noinline))
    2844 do_call( M & @mutex m/*, m2, m3, m4*/@ ) {}
    2845 int main() {
    2846         BENCH(
    2847                 for( N ) do_call( m1/*, m2, m3, m4*/ );
    2848         )
    2849         sout | result`ns;
    2850 }
    2851 \end{cfa}
    2852 \captionof{figure}{\CFA acquire/release mutex benchmark}
    2853 \label{f:mutex}
    2854 
    2855 \columnbreak
    2856 
    2857 \vspace*{-16pt}
    2858 \captionof{table}{Mutex comparison (nanoseconds)}
    2859 \label{tab:mutex}
    2860 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}}
    2861 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
    2862 test and test-and-test lock             & 19.1  & 18.9  & 0.40  \\
    2863 \CFA @mutex@ function, 1 arg.   & 45.9  & 46.6  & 1.45  \\
    2864 \CFA @mutex@ function, 2 arg.   & 105.0 & 104.7 & 3.08  \\
    2865 \CFA @mutex@ function, 4 arg.   & 165.0 & 167.6 & 5.65  \\
    2866 \uC @monitor@ member rtn.               & 54.0  & 53.7  & 0.82  \\
    2867 Java synchronized method                & 31.0  & 31.1  & 0.50  \\
    2868 Pthreads Mutex Lock                             & 33.6  & 32.6  & 1.14
    2869 \end{tabular}
    2870 \end{multicols}
    2871 
    2872 
    2873 \paragraph{External Scheduling}
    2874 
    2875 External scheduling is measured using a cycle of two threads calling and accepting the call using the @waitfor@ statement.
    2876 Figure~\ref{f:ext-sched} shows the code for \CFA, with results in Table~\ref{tab:ext-sched}.
    2877 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects.
    2878 
    2879 \begin{multicols}{2}
    2880 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}}
    2881 \vspace*{-16pt}
    2882 \begin{cfa}
    2883 volatile int go = 0;
    2884 @monitor@ M {} m;
    2885 thread T {};
    2886 void __attribute__((noinline))
    2887 do_call( M & @mutex@ ) {}
    2888 void main( T & ) {
    2889         while ( go == 0 ) { yield(); }
    2890         while ( go == 1 ) { do_call( m ); }
    2891 }
    2892 int __attribute__((noinline))
    2893 do_wait( M & @mutex@ m ) {
    2894         go = 1; // continue other thread
    2895         BENCH( for ( N ) { @waitfor( do_call, m );@ } )
    2896         go = 0; // stop other thread
    2897         sout | result`ns;
    2898 }
    2899 int main() {
    2900         T t;
    2901         do_wait( m );
    2902 }
    2903 \end{cfa}
    2904 \captionof{figure}{\CFA external-scheduling benchmark}
    2905 \label{f:ext-sched}
    2906 
    2907 \columnbreak
    2908 
    2909 \vspace*{-16pt}
    2910 \captionof{table}{External-scheduling comparison (nanoseconds)}
    2911 \label{tab:ext-sched}
    2912 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}}
    2913 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
    2914 \CFA @waitfor@, 1 @monitor@     & 376.4 & 376.8 & 7.63  \\
    2915 \CFA @waitfor@, 2 @monitor@     & 491.4 & 492.0 & 13.31 \\
    2916 \CFA @waitfor@, 4 @monitor@     & 681.0 & 681.7 & 19.10 \\
    2917 \uC @_Accept@                           & 331.1 & 331.4 & 2.66
    2918 \end{tabular}
    2919 \end{multicols}
    2920 
    2921 
    2922 \paragraph{Internal Scheduling}
    2923 
    2924 Internal scheduling is measured using a cycle of two threads signalling and waiting.
    2925 Figure~\ref{f:int-sched} shows the code for \CFA, with results in Table~\ref{tab:int-sched}.
    2926 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects.
    2927 Java scheduling is significantly greater because the benchmark explicitly creates multiple thread in order to prevent the JIT from making the program sequential, \ie removing all locking.
    2928 
    2929 \begin{multicols}{2}
    2930 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}}
    2931 \begin{cfa}
    2932 volatile int go = 0;
    2933 @monitor@ M { @condition c;@ } m;
    2934 void __attribute__((noinline))
    2935 do_call( M & @mutex@ a1 ) { @signal( c );@ }
    2936 thread T {};
    2937 void main( T & this ) {
    2938         while ( go == 0 ) { yield(); }
    2939         while ( go == 1 ) { do_call( m ); }
    2940 }
    2941 int  __attribute__((noinline))
    2942 do_wait( M & mutex m ) with(m) {
    2943         go = 1; // continue other thread
    2944         BENCH( for ( N ) { @wait( c );@ } );
    2945         go = 0; // stop other thread
    2946         sout | result`ns;
    2947 }
    2948 int main() {
    2949         T t;
    2950         do_wait( m );
    2951 }
    2952 \end{cfa}
    2953 \captionof{figure}{\CFA Internal-scheduling benchmark}
    2954 \label{f:int-sched}
    2955 
    2956 \columnbreak
    2957 
    2958 \vspace*{-16pt}
    2959 \captionof{table}{Internal-scheduling comparison (nanoseconds)}
    2960 \label{tab:int-sched}
    2961 \bigskip
    2962 
    2963 \begin{tabular}{@{}r*{3}{D{.}{.}{5.2}}@{}}
    2964 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\
    2965 \CFA @signal@, 1 @monitor@      & 372.6         & 374.3         & 14.17         \\
    2966 \CFA @signal@, 2 @monitor@      & 492.7         & 494.1         & 12.99         \\
    2967 \CFA @signal@, 4 @monitor@      & 749.4         & 750.4         & 24.74         \\
    2968 \uC @signal@                            & 320.5         & 321.0         & 3.36          \\
    2969 Java @notify@                           & 10160.5       & 10169.4       & 267.71        \\
    2970 Pthreads Cond. Variable         & 4949.6        & 5065.2        & 363
    2971 \end{tabular}
    2972 \end{multicols}
    2973 
    2974 
    2975 \section{Conclusion}
     3265\subsection{Discussion}
     3266
     3267Languages using 1:1 threading based on pthreads can at best meet or exceed, due to language overhead, the pthread results.
     3268Note, pthreads has a fast zero-contention mutex lock checked in user space.
     3269Languages with M:N threading have better performance than 1:1 because there is no operating-system interactions (context-switching or locking).
     3270As well, for locking experiments, M:N threading has less contention if only one kernel thread is used.
     3271Languages with stackful coroutines have higher cost than stackless coroutines because of stack allocation and context switching;
     3272however, stackful \uC and \CFA coroutines have approximately the same performance as stackless Python and Node.js generators.
     3273The \CFA stackless generator is approximately 25 times faster for suspend/resume and 200 times faster for creation than stackless Python and Node.js generators.
     3274The Node.js context-switch is costly when asynchronous await must enter the event engine because a promise is not fulfilled.
     3275Finally, the benchmark results correlate across programming languages with and without JIT, indicating the JIT has completed any runtime optimizations.
     3276
     3277
     3278\section{Conclusions and Future Work}
    29763279
    29773280Advanced control-flow will always be difficult, especially when there is temporal ordering and nondeterminism.
    29783281However, many systems exacerbate the difficulty through their presentation mechanisms.
    2979 This paper shows it is possible to present a hierarchy of control-flow features, generator, coroutine, thread, and monitor, providing an integrated set of high-level, efficient, and maintainable control-flow features.
    2980 Eliminated from \CFA are spurious wakeup and barging, which are nonintuitive and lead to errors, and having to work with a bewildering set of low-level locks and acquisition techniques.
    2981 \CFA high-level race-free monitors and tasks provide the core mechanisms for mutual exclusion and synchronization, without having to resort to magic qualifiers like @volatile@/@atomic@.
     3282This paper shows it is possible to understand high-level control-flow using three properties: statefulness, thread, mutual-exclusion/synchronization.
     3283Combining these properties creates a number of high-level, efficient, and maintainable control-flow types: generator, coroutine, thread, each of which can be a monitor.
     3284Eliminated from \CFA are barging and spurious wakeup, which are nonintuitive and lead to errors, and having to work with a bewildering set of low-level locks and acquisition techniques.
     3285\CFA high-level race-free monitors and threads, when used with mutex access function, provide the core mechanisms for mutual exclusion and synchronization, without having to resort to magic qualifiers like @volatile@ or @atomic@.
    29823286Extending these mechanisms to handle high-level deadlock-free bulk acquire across both mutual exclusion and synchronization is a unique contribution.
    29833287The \CFA runtime provides concurrency based on a preemptive M:N user-level threading-system, executing in clusters, which encapsulate scheduling of work on multiple kernel threads providing parallelism.
    29843288The M:N model is judged to be efficient and provide greater flexibility than a 1:1 threading model.
    29853289These concepts and the \CFA runtime-system are written in the \CFA language, extensively leveraging the \CFA type-system, which demonstrates the expressiveness of the \CFA language.
    2986 Performance comparisons with other concurrent systems/languages show the \CFA approach is competitive across all low-level operations, which translates directly into good performance in well-written concurrent applications.
    2987 C programmers should feel comfortable using these mechanisms for developing complex control-flow in applications, with the ability to obtain maximum available performance by selecting mechanisms at the appropriate level of need.
    2988 
    2989 
    2990 \section{Future Work}
     3290Performance comparisons with other concurrent systems and languages show the \CFA approach is competitive across all basic operations, which translates directly into good performance in well-written applications with advanced control-flow.
     3291C programmers should feel comfortable using these mechanisms for developing complex control-flow in applications, with the ability to obtain maximum available performance by selecting mechanisms at the appropriate level of need using only calling communication.
    29913292
    29923293While control flow in \CFA has a strong start, development is still underway to complete a number of missing features.
    29933294
    2994 \paragraph{Flexible Scheduling}
    2995 \label{futur:sched}
    2996 
     3295\medskip
     3296\textbf{Flexible scheduling:}
    29973297An important part of concurrency is scheduling.
    2998 Different scheduling algorithms can affect performance (both in terms of average and variation).
     3298Different scheduling algorithms can affect performance, both in terms of average and variation.
    29993299However, no single scheduler is optimal for all workloads and therefore there is value in being able to change the scheduler for given programs.
    30003300One solution is to offer various tuning options, allowing the scheduler to be adjusted to the requirements of the workload.
    30013301However, to be truly flexible, a pluggable scheduler is necessary.
    3002 Currently, the \CFA pluggable scheduler is too simple to handle complex scheduling, \eg quality of service and real-time, where the scheduler must interact with mutex objects to deal with issues like priority inversion~\cite{Buhr00b}.
    3003 
    3004 \paragraph{Non-Blocking I/O}
    3005 \label{futur:nbio}
    3006 
    3007 Many modern workloads are not bound by computation but IO operations, a common case being web servers and XaaS~\cite{XaaS} (anything as a service).
     3302Currently, the \CFA pluggable scheduler is too simple to handle complex scheduling, \eg quality of service and real time, where the scheduler must interact with mutex objects to deal with issues like priority inversion~\cite{Buhr00b}.
     3303
     3304\smallskip
     3305\textbf{Non-Blocking I/O:}
     3306Many modern workloads are not bound by computation but IO operations, common cases being web servers and XaaS~\cite{XaaS} (anything as a service).
    30083307These types of workloads require significant engineering to amortizing costs of blocking IO-operations.
    3009 At its core, non-blocking I/O is an operating-system level feature queuing IO operations, \eg network operations, and registering for notifications instead of waiting for requests to complete.
     3308At its core, nonblocking I/O is an operating-system level feature queuing IO operations, \eg network operations, and registering for notifications instead of waiting for requests to complete.
    30103309Current trends use asynchronous programming like callbacks, futures, and/or promises, \eg Node.js~\cite{NodeJs} for JavaScript, Spring MVC~\cite{SpringMVC} for Java, and Django~\cite{Django} for Python.
    3011 However, these solutions lead to code that is hard to create, read and maintain.
    3012 A better approach is to tie non-blocking I/O into the concurrency system to provide ease of use with low overhead, \eg thread-per-connection web-services.
    3013 A non-blocking I/O library is currently under development for \CFA.
    3014 
    3015 \paragraph{Other Concurrency Tools}
    3016 \label{futur:tools}
    3017 
     3310However, these solutions lead to code that is hard to create, read, and maintain.
     3311A better approach is to tie nonblocking I/O into the concurrency system to provide ease of use with low overhead, \eg thread-per-connection web-services.
     3312A nonblocking I/O library is currently under development for \CFA.
     3313
     3314\smallskip
     3315\textbf{Other concurrency tools:}
    30183316While monitors offer flexible and powerful concurrency for \CFA, other concurrency tools are also necessary for a complete multi-paradigm concurrency package.
    30193317Examples of such tools can include futures and promises~\cite{promises}, executors and actors.
     
    30213319As well, new \CFA extensions should make it possible to create a uniform interface for virtually all mutual exclusion, including monitors and low-level locks.
    30223320
    3023 \paragraph{Implicit Threading}
    3024 \label{futur:implcit}
    3025 
    3026 Basic concurrent (embarrassingly parallel) applications can benefit greatly from implicit concurrency, where sequential programs are converted to concurrent, possibly with some help from pragmas to guide the conversion.
     3321\smallskip
     3322\textbf{Implicit threading:}
     3323Basic \emph{embarrassingly parallel} applications can benefit greatly from implicit concurrency, where sequential programs are converted to concurrent, with some help from pragmas to guide the conversion.
    30273324This type of concurrency can be achieved both at the language level and at the library level.
    30283325The canonical example of implicit concurrency is concurrent nested @for@ loops, which are amenable to divide and conquer algorithms~\cite{uC++book}.
    3029 The \CFA language features should make it possible to develop a reasonable number of implicit concurrency mechanism to solve basic HPC data-concurrency problems.
     3326The \CFA language features should make it possible to develop a reasonable number of implicit concurrency mechanisms to solve basic HPC data-concurrency problems.
    30303327However, implicit concurrency is a restrictive solution with significant limitations, so it can never replace explicit concurrent programming.
    30313328
     
    30333330\section{Acknowledgements}
    30343331
    3035 The authors would like to recognize the design assistance of Aaron Moss, Rob Schluntz, Andrew Beach and Michael Brooks on the features described in this paper.
    3036 Funding for this project has been provided by Huawei Ltd.\ (\url{http://www.huawei.com}). %, and Peter Buhr is partially funded by the Natural Sciences and Engineering Research Council of Canada.
     3332The authors recognize the design assistance of Aaron Moss, Rob Schluntz, Andrew Beach, and Michael Brooks; David Dice for commenting and helping with the Java benchmarks; and Gregor Richards for helping with the Node.js benchmarks.
     3333This research is funded by the NSERC/Waterloo-Huawei (\url{http://www.huawei.com}) Joint Innovation Lab. %, and Peter Buhr is partially funded by the Natural Sciences and Engineering Research Council of Canada.
    30373334
    30383335{%
    3039 \fontsize{9bp}{12bp}\selectfont%
     3336\fontsize{9bp}{11.5bp}\selectfont%
    30403337\bibliography{pl,local}
    30413338}%
  • doc/papers/concurrency/annex/local.bib

    r3c64c668 r58fe85a  
    2929    booktitle   = {Supercomputing, 2005. Proceedings of the ACM/IEEE SC 2005 Conference},
    3030    publisher   = {IEEE},
     31    location    = {Seattle, Washington, U.S.A.},
     32    month       = nov,
    3133    year        = {2005},
    3234    pages       = {35-35},
    33     month       = nov,
    3435}
    3536
     
    5859
    5960@manual{Cpp-Transactions,
    60         keywords        = {C++, Transactional Memory},
    61         title           = {Technical Specification for C++ Extensions for Transactional Memory},
    62         organization= {International Standard ISO/IEC TS 19841:2015 },
    63         publisher   = {American National Standards Institute},
    64         address = {http://www.iso.org},
    65         year            = 2015,
     61    keywords    = {C++, Transactional Memory},
     62    title       = {Tech. Spec. for C++ Extensions for Transactional Memory {ISO/IEC} {TS} 19841:2015},
     63    organization= {International Standard Organization},
     64    address     = {Geneva, Switzerland},
     65    year        = 2015,
     66    note        = {\href{https://www.iso.org/standard/66343.html}{https://\-www.iso.org/\-standard/\-66343.html}},
    6667}
    6768
     
    109110@manual{affinityLinux,
    110111        key     = {TBB},
    111         title           = "{Linux man page - sched\_setaffinity(2)}"
     112        title           = "{Linux man page - sched\_setaffinity(2)}",
     113        note    = {\href{https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html}{https://\-man7.org/\-linux/man-pages/\-man2/sched\_setaffinity.2.html}},
    112114}
    113115
    114116@manual{affinityWindows,
    115         title           = "{Windows (vs.85) - SetThreadAffinityMask function}"
     117        title           = "{Windows documentation - SetThreadAffinityMask function}",
     118        note    = {\href{https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadaffinitymask}{https://\-docs.microsoft.com/\-en-us/\-windows/\-win32/api/\-winbase/\-nf-winbase-setthreadaffinitymask}}
    116119}
    117120
  • doc/papers/concurrency/examples/Fib.py

    r3c64c668 r58fe85a  
    44        while True:
    55                fn = fn1 + fn2; fn2 = fn1; fn1 = fn; yield fn
    6 
    7 
    86
    97f1 = Fib()
     
    1412# Local Variables: #
    1513# tab-width: 4 #
    16 # compile-command: "python3.5 Fib.py" #
     14# compile-command: "python3.7 Fib.py" #
    1715# End: #
  • doc/papers/concurrency/examples/Fib2.c

    r3c64c668 r58fe85a  
    11#include <stdio.h>
    22
    3 void mary() {
    4         printf( "MARY\n" );
    5 }
    6 
    73#define FIB_INIT { 0 }
    8 typedef struct { int next; int fn1, fn2; } Fib;
     4typedef struct { int restart; int fn1, fn2; } Fib;
    95int fib( Fib * f ) {
    10         static void * states[] = { &&s1, &&s2, &&s3 };
    11         goto *states[f->next];
     6        static void * states[] = { &&s0, &&s1, &&s2 };
     7        goto *states[f->restart];
     8  s0:
     9        f->fn1 = 0;
     10        f->restart = 1;
     11        return f->fn1;
    1212  s1:
    13         mary();
    14         f->fn1 = 0;
    15         f->next = 1;
    16         return f->fn1;
    17   s2:
    18         mary();
    1913        f->fn2 = f->fn1;
    2014        f->fn1 = 1;
    21         f->next = 2;
     15        f->restart = 2;
    2216        return f->fn1;
    23   s3:;
    24         mary();
     17  s2:;
    2518        int fn = f->fn1 + f->fn2;
    2619        f->fn2 = f->fn1;
  • doc/papers/concurrency/examples/Fib2.py

    r3c64c668 r58fe85a  
    11def Fib():
    2     fn1, fn = 0, 1
     2    fn1, fn = 1, 0
    33    while True:
    4         yield fn1
     4        yield fn
    55        fn1, fn = fn, fn1 + fn
    66
     
    1212# Local Variables: #
    1313# tab-width: 4 #
    14 # compile-command: "python3.5 Fib2.py" #
     14# compile-command: "python3.7 Fib2.py" #
    1515# End: #
  • doc/papers/concurrency/examples/Fib3.c

    r3c64c668 r58fe85a  
    22
    33typedef struct {
    4         int fn1, fn;
    5         void * next;
     4        int restart, fn1, fn;
    65} Fib;
    7 #define FibCtor { 1, 0, NULL }
     6#define FibCtor { 0, 1, 0 }
    87
    98Fib * comain( Fib * f ) {
    10         if ( __builtin_expect(f->next != 0, 1) ) goto *f->next;
    11         f->next = &&s1;
     9        static void * states[] = {&&s0, &&s1};
     10        goto *states[f->restart];
     11  s0: f->restart = 1;
    1212        for ( ;; ) {
    1313                return f;
  • doc/papers/concurrency/examples/FibRefactor.py

    r3c64c668 r58fe85a  
    2222# Local Variables: #
    2323# tab-width: 4 #
    24 # compile-command: "python3.5 FibRefactor.py" #
     24# compile-command: "python3.7 FibRefactor.py" #
    2525# End: #
  • doc/papers/concurrency/examples/Format.c

    r3c64c668 r58fe85a  
    22
    33typedef struct {
    4         void * next;
     4        int restart, g, b;
    55        char ch;
    6         int g, b;
    76} Fmt;
    87
    98void comain( Fmt * f ) {
    10         if ( __builtin_expect(f->next != 0, 1) ) goto *f->next;
    11         f->next = &&s1;
     9        static void * states[] = {&&s0, &&s1};
     10        goto *states[f->restart];
     11  s0: f->restart = 1;
    1212        for ( ;; ) {
    1313                for ( f->g = 0; f->g < 5; f->g += 1 ) {                 // groups
    1414                        for ( f->b = 0; f->b < 4; f->b += 1 ) {         // blocks
    15                                 return;
    16                           s1:;  while ( f->ch == '\n' ) return;         // ignore
     15                                do {
     16                                        return;  s1: ;
     17                                } while ( f->ch == '\n' );                              // ignore
    1718                                printf( "%c", f->ch );                                  // print character
    1819                        }
     
    2425
    2526int main() {
    26         Fmt fmt = { NULL };
     27        Fmt fmt = { 0 };
    2728        comain( &fmt );                                                                         // prime
    2829        for ( ;; ) {
  • doc/papers/concurrency/examples/Format.cc

    r3c64c668 r58fe85a  
    66                        for ( g = 0; g < 5; g += 1 ) { // groups of 5 blocks
    77                                for ( b = 0; b < 4; b += 1 ) { // blocks of 4 characters
    8 //                                      for ( ;; ) { // for newline characters
     8                                        for ( ;; ) { // for newline characters
    99                                                suspend();
    10 //                                              if ( ch != '\n' ) break; // ignore newline
    11 //                                      }
     10                                                if ( ch != '\n' ) break; // ignore newline
     11                                        }
    1212//                                      cout << ch; // print character
    1313                                }
     
    3131// Local Variables: //
    3232// tab-width: 4 //
    33 // compile-command: "u++-work -O2 -nodebubg Format.cc" //
     33// compile-command: "u++-work -O2 -nodebug Format.cc" //
    3434// End: //
  • doc/papers/concurrency/examples/Format.cfa

    r3c64c668 r58fe85a  
    1111                for ( g = 0; g < 5; g += 1 ) {          // groups of 5 blocks
    1212                        for ( b = 0; b < 4; b += 1 ) {  // blocks of 4 characters
    13 //                              do {
     13                                do {
    1414                                        suspend();
    15 //                              } while ( ch == '\n' || ch == '\t' );
     15                                } while ( ch == '\n' || ch == '\t' );
    1616                                sout | ch;                                      // print character
    1717                        }
  • doc/papers/concurrency/examples/Format.data

    r3c64c668 r58fe85a  
    1 abcdefghijklmnopqrstuvwxyzxxxxxxxxxxxxxx
     1abcdefghijklmnop
     2qrstuvwxyzx
     3xxxxxxxxxxxxx
  • doc/papers/concurrency/examples/Format.py

    r3c64c668 r58fe85a  
    44                        for g in range( 5 ):    # groups of 5 blocks
    55                                for b in range( 4 ): # blocks of 4 characters
    6                                         print( (yield), end='' ) # receive from send
     6                                        while True:
     7                                                ch = (yield) # receive from send
     8                                                if '\n' not in ch:
     9                                                        break
     10                                        print( ch, end='' ) # receive from send
    711                                print( '  ', end='' ) # block separator
    812                        print()                                 # group separator
     
    1115                        print()
    1216
     17input = "abcdefghijklmnop\nqrstuvwx\nyzxxxxxxxxxxxxxx\n"
     18
    1319fmt = Format()
    1420next( fmt )                                                     # prime generator
    15 for i in range( 41 ):
    16         fmt.send( 'a' );                                # send to yield
     21for i in input:
     22        fmt.send( i );                          # send to yield
    1723
    1824# Local Variables: #
    1925# tab-width: 4 #
    20 # compile-command: "python3.5 Format.py" #
     26# compile-command: "python3.7 Format.py" #
    2127# End: #
  • doc/papers/concurrency/examples/Format1.c

    r3c64c668 r58fe85a  
    22
    33typedef struct {
    4         void * next;
     4        int restart, g, b;
    55        char ch;
    6         int g, b;
    76} Fmt;
    87
    98void format( Fmt * f ) {
    10         if ( __builtin_expect(f->next != 0, 1) ) goto *f->next;
    11         f->next = &&s1;
     9        static void * states[] = {&&s0, &&s1};
     10        goto *states[f->restart];
     11  s0: f->restart = 1;
    1212        for ( ;; ) {
    1313                for ( f->g = 0; f->g < 5; f->g += 1 ) {                 // groups
    1414                        for ( f->b = 0; f->b < 4; f->b += 1 ) {         // blocks
    1515                                return;
    16                           s1: ;
    17                                 if ( f->ch == '\0' ) goto fini;                 // EOF ?
     16                          s1: if ( f->ch == '\0' ) goto fini;           // EOF ?
    1817                                while ( f->ch == '\n' ) return;                 // ignore
    19                                 printf( "%c", f->ch );                                  // print character
     18//                              printf( "%c", f->ch );                                  // print character
    2019                        }
    21                         printf( " " );                                                          // block separator
     20//                      printf( " " );                                                          // block separator
    2221                }
    23                 printf( "\n" );                                                                 // group separator
     22//              printf( "\n" );                                                                 // group separator
    2423        }
    25   fini:
    26         if ( f->g != 0 || f->b != 0 ) printf( "\n" );
     24  fini:;
     25//      if ( f->g != 0 || f->b != 0 ) printf( "\n" );
    2726}
    2827
    2928int main() {
    30         Fmt fmt = { NULL };
     29        Fmt fmt = { 0 };
    3130        format( &fmt );                                                                         // prime
    32         for ( ;; ) {
    33                 scanf( "%c", &fmt.ch );                                                 // direct read into communication variable
    34           if ( feof( stdin ) ) break;
     31        fmt.ch = 'a';
     32        for ( long int i = 0; i < 1000000000; i += 1 ) {
     33//              scanf( "%c", &fmt.ch );                                                 // direct read into communication variable
     34//        if ( feof( stdin ) ) break;
    3535                format( &fmt );
    3636        }
    37         fmt.ch = '\0';
     37        fmt.ch = '\0';                                                                          // sentential (EOF)
    3838        format( &fmt );
    3939}
  • doc/papers/concurrency/examples/PingPong.c

    r3c64c668 r58fe85a  
    22
    33typedef struct PingPong {
     4        int restart;                                                                            // style 1
     5        int N, i;
    46        const char * name;
    5         int N, i;
    67        struct PingPong * partner;
    7         void * next;
     8        void * next;                                                                            // style 2
    89} PingPong;
    9 #define PPCtor( name, N ) { name, N, 0, NULL, NULL }
     10#define PPCtor( name, N ) { 0, N, 0, name, NULL, NULL }
     11
    1012void comain( PingPong * pp ) __attribute__(( noinline ));
    1113void comain( PingPong * pp ) {
     14#if 0
    1215        if ( __builtin_expect(pp->next != 0, 1) ) goto *pp->next;
    13 #if 0
    14         pp->next = &&here;
    15                 asm( "mov  %0,%%rdi" : "=m" (pp) );
    16                 asm( "mov  %rdi,%rax" );
    17 #ifndef OPT
    18 #ifdef PRINT
    19                 asm( "add  $16, %rsp" );
    20 #endif // PRINT
    21                 asm( "popq %rbp" );
    22 #endif // ! OPT
    23 
    24 #ifdef OPT
    25 #ifdef PRINT
    26                 asm( "popq %rbx" );
    27 #endif // PRINT
    28 #endif // OPT
    29                 asm( "jmp  comain" );
    30   here: ;
    31 #endif // 0
    32 
    3316        pp->next = &&cycle;
    3417        for ( ; pp->i < pp->N; pp->i += 1 ) {
     
    5336          cycle: ;
    5437        } // for
     38#endif // 0
     39
     40#if 1
     41        static void * states[] = {&&s0, &&s1};
     42        goto *states[pp->restart];
     43  s0: pp->restart = 1;
     44        for ( ; pp->i < pp->N; pp->i += 1 ) {
     45#ifdef PRINT
     46                printf( "%s %d\n", pp->name, pp->i );
     47#endif // PRINT
     48                asm( "mov  %0,%%rdi" : "=m" (pp->partner) );
     49                asm( "mov  %rdi,%rax" );
     50#ifndef OPT
     51#ifdef PRINT
     52                asm( "add  $16, %rsp" );
     53#endif // PRINT
     54                asm( "popq %rbp" );
     55#endif // ! OPT
     56
     57#ifdef OPT
     58#ifdef PRINT
     59                asm( "popq %rbx" );
     60#endif // PRINT
     61#endif // OPT
     62                asm( "jmp  comain" );
     63          s1: ;
     64        } // for
     65#endif // 0
    5566}
    5667
     
    7081// Local Variables: //
    7182// tab-width: 4 //
    72 // compile-command: "gcc-8 -g -DPRINT PingPong.c" //
     83// compile-command: "gcc-9 -g -DPRINT PingPong.c" //
    7384// End: //
  • doc/papers/concurrency/examples/Pingpong.py

    r3c64c668 r58fe85a  
    11def PingPong( name, N ):
    2         partner = (yield)           # get partner
    3         yield                       # resume scheduler
     2        partner = yield                         # get partner
     3        yield                                           # resume scheduler
    44        for i in range( N ):
    55                print( name )
    6                 yield partner           # execute next
     6                yield partner                   # execute next
    77        print( "end", name )
    88
    99def Scheduler():
    10         n = (yield)                 # starting coroutine
    11         while True:
    12                 n = next( n )           # schedule coroutine
     10        n = yield                                       # starting coroutine
     11        try:
     12                while True:
     13                        n = next( n )           # schedule coroutine
     14        except StopIteration:
     15                pass
    1316
    1417pi = PingPong( "ping", 5 )
    1518po = PingPong( "pong", 5 )
    16 next( pi )                      # prime
    17 pi.send( po )                   # send partner
    18 next( po )                      # prime
    19 po.send( pi )                   # send partner
     19next( pi )                                              # prime
     20pi.send( po )                                   # send partner
     21next( po )                                              # prime
     22po.send( pi )                                   # send partner
    2023
    2124s = Scheduler();
    22 next( s )                       # prime
     25next( s )                                               # prime
    2326try:
    2427        s.send( pi )                            # start cycle
    25 except StopIteration:
    26         print( "scheduler stop" )
     28except StopIteration:                   # scheduler stopped
     29        pass
    2730print( "stop" )
    2831
    2932# Local Variables: #
    3033# tab-width: 4 #
    31 # compile-command: "python3.5 Pingpong.py" #
     34# compile-command: "python3.7 Pingpong.py" #
    3235# End: #
  • doc/papers/concurrency/examples/ProdCons.py

    r3c64c668 r58fe85a  
    11def Prod( N ):
    2         cons = (yield)              # get cons
    3         yield                       # resume scheduler
     2        cons = yield                            # get cons
     3        yield                                           # resume scheduler
    44        for i in range( N ):
    55                print( "prod" )
    6                 yield cons              # execute next
     6                yield cons                              # execute next
    77        print( "end", "prod" )
    88
    99def Cons( N ):
    10         prod = (yield)              # get prod
    11         yield                       # resume scheduler
     10        prod = yield                            # get prod
     11        yield                                           # resume scheduler
    1212        for i in range( N ):
    1313                print( "cons" )
    14                 yield prod              # execute next
     14                yield prod                              # execute next
    1515        print( "end", "cons" )
    1616
    1717def Scheduler():
    18         n = (yield)                 # starting coroutine
    19         while True:
    20                 n = next( n )           # schedule coroutine
     18        n = yield                                       # starting coroutine
     19        try:
     20                while True:
     21                        n = next( n )           # schedule coroutine
     22        except StopIteration:
     23                pass
    2124
    2225prod = Prod( 5 )
    2326cons = Cons( 5 )
    24 next( prod )                    # prime
    25 prod.send( cons )               # send cons
    26 next( cons )                    # prime
    27 cons.send( prod )               # send prod
     27next( prod )                                    # prime
     28prod.send( cons )                               # send cons
     29next( cons )                                    # prime
     30cons.send( prod )                               # send prod
    2831
    2932s = Scheduler();
    30 next( s )                       # prime
     33next( s )                                               # prime
    3134try:
    3235        s.send( prod )                          # start cycle
    33 except StopIteration:
    34         print( "scheduler stop" )
     36except StopIteration:                   # scheduler stopped
     37        pass
    3538print( "stop" )
    3639
    3740# Local Variables: #
    3841# tab-width: 4 #
    39 # compile-command: "python3.5 ProdCons.py" #
     42# compile-command: "python3.7 ProdCons.py" #
    4043# End: #
  • doc/papers/concurrency/examples/Refactor.py

    r3c64c668 r58fe85a  
    2626# Local Variables: #
    2727# tab-width: 4 #
    28 # compile-command: "python3.5 Refactor.py" #
     28# compile-command: "python3.7 Refactor.py" #
    2929# End: #
  • doc/papers/concurrency/figures/FullCoroutinePhases.fig

    r3c64c668 r58fe85a  
    88-2
    991200 2
    10 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 4575.000 2437.500 4275 1875 4575 1800 4875 1875
     105 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 5175.000 2437.500 4875 1875 5175 1800 5475 1875
    1111        1 1 1.00 45.00 90.00
    12 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 4575.000 1537.500 4875 2100 4575 2175 4275 2100
     125 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 5175.000 1537.500 5475 2100 5175 2175 4875 2100
    1313        1 1 1.00 45.00 90.00
    14 5 1 0 1 0 7 50 -1 -1 0.000 0 1 1 0 4207.500 1642.500 4125 1425 3975 1650 4200 1875
     145 1 0 1 0 7 50 -1 -1 0.000 0 1 1 0 4807.500 1642.500 4725 1425 4575 1650 4800 1875
    1515        1 1 1.00 45.00 90.00
     166 1575 1575 2700 2025
    16172 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
    1718        1 1 1.00 45.00 90.00
     
    2021        1 1 1.00 45.00 90.00
    2122         2175 1575 2400 1800
     234 1 0 100 0 4 10 0.0000 2 165 300 1725 1950 ping\001
     244 1 0 100 0 4 10 0.0000 2 135 360 2475 1950 pong\001
     25-6
     266 3075 1575 4200 2025
     276 3075 1575 4200 2025
    22282 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
    2329        1 1 1.00 45.00 90.00
    24          3300 1575 3300 1800
     30         3525 1575 3300 1800
    25312 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
    2632        1 1 1.00 45.00 90.00
    27          3300 2025 3300 2250
    28 4 1 0 100 0 0 10 0.0000 2 105 555 2100 1200 creation\001
    29 4 1 0 100 0 4 10 0.0000 2 165 300 1725 1950 ping\001
    30 4 1 0 100 0 4 10 0.0000 2 135 360 2475 1950 pong\001
    31 4 1 0 100 0 4 10 0.0000 2 165 300 3300 1950 ping\001
    32 4 1 0 100 0 4 10 0.0000 2 135 360 3300 2400 pong\001
    33 4 1 0 100 0 0 10 0.0000 2 105 675 4575 1200 execution\001
    34 4 1 0 100 0 4 10 0.0000 2 165 300 4275 2025 ping\001
    35 4 1 0 100 0 4 10 0.0000 2 135 360 4875 2025 pong\001
    36 4 1 0 100 0 0 10 0.0000 2 90 420 3300 1200 starter\001
     33         3675 1575 3900 1800
     344 1 0 100 0 4 10 0.0000 2 165 300 3225 1950 ping\001
     354 1 0 100 0 4 10 0.0000 2 135 360 3975 1950 pong\001
     36-6
     37-6
    37384 1 0 100 0 4 10 0.0000 2 165 705 2100 1500 pgm main\001
    38 4 1 0 100 0 4 10 0.0000 2 165 705 3300 1500 pgm main\001
    39 4 1 0 100 0 4 10 0.0000 2 165 705 4500 1500 pgm main\001
     394 1 0 100 0 4 10 0.0000 2 165 705 3600 1500 pgm main\001
     404 1 0 100 0 4 10 0.0000 2 165 300 4875 2025 ping\001
     414 1 0 100 0 4 10 0.0000 2 135 360 5475 2025 pong\001
     424 1 0 100 0 4 10 0.0000 2 165 705 5100 1500 pgm main\001
     434 1 0 100 0 2 10 0.0000 2 105 540 2100 1275 creator\001
     444 1 0 100 0 2 10 0.0000 2 105 495 3600 1275 starter\001
     454 1 0 100 0 2 10 0.0000 2 105 690 5175 1275 execution\001
  • doc/papers/concurrency/figures/RunTimeStructure.fig

    r3c64c668 r58fe85a  
    88-2
    991200 2
    10 6 3855 2775 4155 2925
    11 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3930 2850 30 30 3930 2850 3960 2880
    12 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4035 2850 30 30 4035 2850 4065 2880
     106 3255 2475 3555 2625
     111 3 0 1 0 0 0 0 0 0.000 1 0.0000 3330 2550 30 30 3330 2550 3360 2580
     121 3 0 1 0 0 0 0 0 0.000 1 0.0000 3435 2550 30 30 3435 2550 3465 2580
    1313-6
    14 6 4755 3525 5055 3675
    15 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4830 3600 30 30 4830 3600 4860 3630
    16 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4935 3600 30 30 4935 3600 4965 3630
     146 4155 3225 4455 3375
     151 3 0 1 0 0 0 0 0 0.000 1 0.0000 4230 3300 30 30 4230 3300 4260 3330
     161 3 0 1 0 0 0 0 0 0.000 1 0.0000 4335 3300 30 30 4335 3300 4365 3330
    1717-6
    18 6 4650 2775 4950 2925
    19 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4725 2850 15 15 4725 2850 4740 2865
    20 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4800 2850 15 15 4800 2850 4815 2865
    21 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4875 2850 15 15 4875 2850 4890 2865
     186 4050 2475 4350 2625
     191 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4125 2550 15 15 4125 2550 4140 2565
     201 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4200 2550 15 15 4200 2550 4215 2565
     211 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4275 2550 15 15 4275 2550 4290 2565
    2222-6
    23 6 3225 2400 3525 2550
    24 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3300 2475 15 15 3300 2475 3315 2490
    25 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3375 2475 15 15 3375 2475 3390 2490
    26 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3450 2475 15 15 3450 2475 3465 2490
     236 2625 2100 2925 2250
     241 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2700 2175 15 15 2700 2175 2715 2190
     251 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2775 2175 15 15 2775 2175 2790 2190
     261 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2850 2175 15 15 2850 2175 2865 2190
    2727-6
    28 6 5475 3450 5625 3750
    29 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3525 15 15 5550 3525 5535 3540
    30 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3600 15 15 5550 3600 5535 3615
    31 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3675 15 15 5550 3675 5535 3690
     286 4875 3150 5025 3450
     291 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3225 15 15 4950 3225 4935 3240
     301 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3300 15 15 4950 3300 4935 3315
     311 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3375 15 15 4950 3375 4935 3390
    3232-6
    33 6 4275 3525 4575 3675
    34 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4350 3600 15 15 4350 3600 4365 3615
    35 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4425 3600 15 15 4425 3600 4440 3615
    36 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4500 3600 15 15 4500 3600 4515 3615
     336 3675 3225 3975 3375
     341 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3750 3300 15 15 3750 3300 3765 3315
     351 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3825 3300 15 15 3825 3300 3840 3315
     361 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3900 3300 15 15 3900 3300 3915 3315
    3737-6
    38 6 2175 4650 7050 4950
    39 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 2250 4830 30 30 2250 4830 2280 4860
    40 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4200 4800 150 75 4200 4800 4350 4875
    41 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3275 4800 100 100 3275 4800 3375 4800
    42 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
    43          5400 4950 5400 4725 5175 4725 5175 4950 5400 4950
    44 2 2 1 1 -1 -1 0 0 -1 3.000 0 0 0 0 0 5
    45          6525 4950 6300 4950 6300 4725 6525 4725 6525 4950
    46 4 0 -1 0 0 0 10 0.0000 2 105 450 6600 4875 cluster\001
    47 4 0 -1 0 0 0 10 0.0000 2 105 660 5475 4875 processor\001
    48 4 0 -1 0 0 0 10 0.0000 2 105 555 4425 4875 monitor\001
    49 4 0 -1 0 0 0 10 0.0000 2 120 270 3450 4875 task\001
    50 4 0 -1 0 0 0 10 0.0000 2 105 660 2325 4875 coroutine\001
     386 2625 3825 4050 4125
     396 3750 3900 4050 4050
     401 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3825 3975 15 15 3825 3975 3840 3990
     411 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3900 3975 15 15 3900 3975 3915 3990
     421 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3975 3975 15 15 3975 3975 3990 3990
    5143-6
    52 6 3450 1275 3750 1425
    53 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3525 1350 15 15 3525 1350 3540 1365
    54 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3600 1350 15 15 3600 1350 3615 1365
    55 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3675 1350 15 15 3675 1350 3690 1365
     441 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 2850 3975 225 150 2850 3975 3075 4125
     451 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3450 3975 225 150 3450 3975 3675 4125
    5646-6
    57 6 5550 1275 5850 1425
    58 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5625 1350 15 15 5625 1350 5640 1365
    59 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5700 1350 15 15 5700 1350 5715 1365
    60 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5775 1350 15 15 5775 1350 5790 1365
     476 6075 3825 6900 4125
     486 6600 3900 6900 4050
     491 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 6675 3975 15 15 6675 3975 6690 3990
     501 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 6750 3975 15 15 6750 3975 6765 3990
     511 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 6825 3975 15 15 6825 3975 6840 3990
    6152-6
    62 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 2625 150 150 5550 2625 5700 2625
    63 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 3225 150 150 5550 3225 5700 3225
    64 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 3975 150 150 5550 3975 5700 3975
    65 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3525 2850 150 150 3525 2850 3675 2850
    66 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4200 2475 150 150 4200 2475 4350 2475
    67 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4425 2850 150 150 4425 2850 4575 2850
    68 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4650 2475 150 150 4650 2475 4800 2475
    69 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3525 3600 150 150 3525 3600 3675 3600
    70 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3975 3600 150 150 3975 3600 4125 3600
    71 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3525 3600 30 30 3525 3600 3555 3630
    72 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3750 2475 150 150 3750 2475 3900 2625
    73 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4875 3600 150 150 4875 3600 5025 3750
    74 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3975 2850 150 150 3975 2850 4125 2850
    75 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 7200 2775 150 150 7200 2775 7350 2775
    76 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4650 1350 225 150 4650 1350 4875 1500
    77 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5250 1350 225 150 5250 1350 5475 1500
    78 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 1350 225 150 4050 1350 4275 1500
    79 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
    80          2400 4200 2400 3750 1950 3750 1950 4200 2400 4200
    81 2 2 1 1 -1 -1 0 0 -1 4.000 0 0 0 0 0 5
    82          6300 4500 6300 1800 3000 1800 3000 4500 6300 4500
    83 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
    84          5775 2850 5775 2400 5325 2400 5325 2850 5775 2850
    85 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
    86          5775 4200 5775 3750 5325 3750 5325 4200 5775 4200
     531 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 6300 3975 225 150 6300 3975 6525 4125
     54-6
     556 6075 3225 7425 3675
    87562 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    8857        1 1 1.00 45.00 90.00
    89          5175 3975 5325 3975
     58         6075 3450 6375 3450
    90592 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    9160        1 1 1.00 45.00 90.00
    92          5175 3225 5325 3225
     61         6525 3450 6750 3450
     622 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
     63         7200 3675 7200 3225 6750 3225 6750 3675 7200 3675
    93642 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    9465        1 1 1.00 45.00 90.00
    95          5175 2625 5325 2625
     66         7200 3450 7425 3450
     67-6
     681 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4950 2325 150 150 4950 2325 5100 2325
     691 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4950 2925 150 150 4950 2925 5100 2925
     701 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4950 3675 150 150 4950 3675 5100 3675
     711 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 2925 2550 150 150 2925 2550 3075 2550
     721 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3600 2175 150 150 3600 2175 3750 2175
     731 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3825 2550 150 150 3825 2550 3975 2550
     741 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 2175 150 150 4050 2175 4200 2175
     751 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3375 3300 150 150 3375 3300 3525 3300
     761 3 0 1 0 0 0 0 0 0.000 1 0.0000 2925 3300 30 30 2925 3300 2955 3330
     771 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3150 2175 150 150 3150 2175 3300 2325
     781 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4275 3300 150 150 4275 3300 4425 3450
     791 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3375 2550 150 150 3375 2550 3525 2550
     801 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 6600 2475 150 150 6600 2475 6750 2475
     811 3 0 1 0 0 0 0 0 0.000 1 0.0000 1650 4530 30 30 1650 4530 1680 4560
     821 3 0 1 0 0 0 0 0 0.000 1 0.0000 6600 2475 30 30 6600 2475 6630 2505
     831 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 2925 3300 150 150 2925 3300 3075 3300
     841 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3275 4500 100 100 3275 4500 3375 4500
     851 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 4500 150 75 4050 4500 4200 4575
     862 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
     87         1800 3900 1800 3450 1350 3450 1350 3900 1800 3900
     882 2 1 1 -1 -1 0 0 -1 4.000 0 0 0 0 0 5
     89         5700 4200 5700 1500 2400 1500 2400 4200 5700 4200
     902 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
     91         5175 2550 5175 2100 4725 2100 4725 2550 5175 2550
     922 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
     93         5175 3900 5175 3450 4725 3450 4725 3900 5175 3900
    96942 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    9795        1 1 1.00 45.00 90.00
    98          5775 3975 5925 3975
     96         4575 3675 4725 3675
    99972 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    10098        1 1 1.00 45.00 90.00
    101          5775 3225 5925 3225
     99         4575 2925 4725 2925
    1021002 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    103101        1 1 1.00 45.00 90.00
    104          5775 2625 5925 2625
    105 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2
    106          5175 3975 5175 2625
     102         4575 2325 4725 2325
    1071032 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    108104        1 1 1.00 45.00 90.00
    109          5925 3975 5925 2025
     105         5175 3675 5325 3675
    1101062 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    111107        1 1 1.00 45.00 90.00
    112          5925 3750 6225 3750
     108         5175 2925 5325 2925
    1131092 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    114110        1 1 1.00 45.00 90.00
    115          3450 2625 3225 2625
     111         5175 2325 5325 2325
     1122 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2
     113         4575 3675 4575 2325
     1142 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
     115        1 1 1.00 45.00 90.00
     116         5325 3675 5325 1725
     1172 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
     118        1 1 1.00 45.00 90.00
     119         5325 3450 5625 3450
     1202 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
     121        1 1 1.00 45.00 90.00
     122         2850 2325 2625 2325
    1161232 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 3
    117124        1 1 1.00 45.00 90.00
    118          5925 2025 4200 2025 4200 2250
     125         5325 1725 3600 1725 3600 1950
    1191262 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2
    120          3225 2625 3225 3600
     127         2625 2325 2625 3300
    1211282 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    122129        1 1 1.00 45.00 90.00
    123          3075 3600 3375 3600
     130         2475 3300 2775 3300
    1241312 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    125132        1 1 1.00 45.00 90.00
    126          3675 3600 3825 3600
     133         3075 3300 3225 3300
    1271342 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    128135        1 1 1.00 45.00 90.00
    129          4125 3600 4275 3600
     136         3525 3300 3675 3300
    1301372 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    131138        1 1 1.00 45.00 90.00
    132          4575 3600 4725 3600
     139         3975 3300 4125 3300
    1331402 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    134141        1 1 1.00 45.00 90.00
    135          5025 3600 5175 3600
     142         4425 3300 4575 3300
    1361432 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
    137          5775 3450 5775 3000 5325 3000 5325 3450 5775 3450
     144         5175 3150 5175 2700 4725 2700 4725 3150 5175 3150
    1381452 2 1 1 -1 -1 0 0 -1 4.000 0 0 0 0 0 5
    139          8100 4500 8100 1800 6600 1800 6600 4500 8100 4500
     146         7500 4200 7500 1500 6000 1500 6000 4200 7500 4200
    1401472 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    141148        1 1 1.00 45.00 90.00
    142          6675 3975 6975 3975
    143 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    144         1 1 1.00 45.00 90.00
    145          7050 2775 6825 2775
     149         6450 2475 6225 2475
    1461502 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2
    147          6825 2775 6825 3975
    148 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    149         1 1 1.00 45.00 90.00
    150          7125 3975 7350 3975
    151 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
    152          7800 4200 7800 3750 7350 3750 7350 4200 7800 4200
    153 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2
    154         1 1 1.00 45.00 90.00
    155          7800 3975 8025 3975
     151         6225 2475 6225 3450
    1561522 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 4
    157153        1 1 1.00 45.00 90.00
    158          7875 3975 7875 2325 7200 2325 7200 2550
    159 4 1 -1 0 0 0 10 0.0000 2 105 720 5550 4425 Processors\001
    160 4 1 -1 0 0 0 10 0.0000 2 120 1005 4200 3225 Blocked Tasks\001
    161 4 1 -1 0 0 0 10 0.0000 2 150 870 4200 3975 Ready Tasks\001
    162 4 1 -1 0 0 0 10 0.0000 2 135 1095 7350 1725 Other Cluster(s)\001
    163 4 1 -1 0 0 0 10 0.0000 2 105 840 4650 1725 User Cluster\001
    164 4 1 -1 0 0 0 10 0.0000 2 150 615 2175 3675 Manager\001
    165 4 1 -1 0 0 0 10 0.0000 2 105 990 2175 3525 Discrete-event\001
    166 4 1 -1 0 0 0 10 0.0000 2 135 795 2175 4350 preemption\001
     154         7275 3450 7275 2025 6600 2025 6600 2250
     1552 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5
     156         5250 4650 5250 4425 5025 4425 5025 4650 5250 4650
     1572 2 1 1 -1 -1 0 0 -1 3.000 0 0 0 0 0 5
     158         6375 4650 6150 4650 6150 4425 6375 4425 6375 4650
     1594 1 -1 0 0 0 10 0.0000 2 105 720 4950 4125 Processors\001
     1604 1 -1 0 0 0 10 0.0000 2 120 1005 3600 2925 Blocked Tasks\001
     1614 1 -1 0 0 0 10 0.0000 2 150 870 3600 3675 Ready Tasks\001
     1624 1 -1 0 0 0 10 0.0000 2 135 1095 6750 1425 Other Cluster(s)\001
     1634 1 -1 0 0 0 10 0.0000 2 105 840 4050 1425 User Cluster\001
     1644 1 -1 0 0 0 10 0.0000 2 150 615 1575 3375 Manager\001
     1654 1 -1 0 0 0 10 0.0000 2 105 990 1575 3225 Discrete-event\001
     1664 1 -1 0 0 0 10 0.0000 2 135 795 1575 4050 preemption\001
     1674 0 -1 0 0 0 10 0.0000 2 150 1365 1725 4575 generator/coroutine\001
     1684 0 -1 0 0 0 10 0.0000 2 120 270 3450 4575 task\001
     1694 0 -1 0 0 0 10 0.0000 2 105 450 6450 4575 cluster\001
     1704 0 -1 0 0 0 10 0.0000 2 105 660 5325 4575 processor\001
     1714 0 -1 0 0 0 10 0.0000 2 105 555 4275 4575 monitor\001
  • doc/papers/concurrency/mail

    r3c64c668 r58fe85a  
    1010Dear Dr Buhr,
    1111
    12 Your manuscript entitled "Concurrency in Cāˆ€" has been received by Software:
     12Your manuscript entitled "Concurrency in Cforall" has been received by Software:
    1313Practice and Experience. It will be given full consideration for publication in
    1414the journal.
     
    4141Dear Dr Buhr,
    4242
    43 Many thanks for submitting SPE-18-0205 entitled "Concurrency in Cāˆ€" to Software: Practice and Experience.
     43Many thanks for submitting SPE-18-0205 entitled "Concurrency in Cforall" to Software: Practice and Experience.
    4444
    4545In view of the comments of the referees found at the bottom of this letter, I cannot accept your paper for publication in Software: Practice and Experience. I hope that you find the referees' very detailed comments helpful.
  • doc/papers/concurrency/mail2

    r3c64c668 r58fe85a  
    2222Software: Practice and Experience Editorial Office
    2323
     24
     25
     26Date: Tue, 12 Nov 2019 22:25:17 +0000
     27From: Richard Jones <onbehalfof@manuscriptcentral.com>
     28Reply-To: R.E.Jones@kent.ac.uk
     29To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca
     30Subject: Software: Practice and Experience - Decision on Manuscript ID
     31 SPE-19-0219
     32
     3312-Nov-2019
     34
     35Dear Dr Buhr,
     36
     37Many thanks for submitting SPE-19-0219 entitled "Advanced Control-flow and Concurrency in Cforall" to Software: Practice and Experience. The paper has now been reviewed and the comments of the referees are included at the bottom of this letter.
     38
     39The decision on this paper is that it requires substantial further work is required. The referees have a number of substantial concerns. All the reviewers found the submission very hard to read; two of the reviewers state that it needs very substantial restructuring. These concerns must be addressed before your submission can be considered further.
     40
     41A revised version of your manuscript that takes into account the comments of the referees will be reconsidered for publication.
     42
     43Please note that submitting a revision of your manuscript does not guarantee eventual acceptance, and that your revision will be subject to re-review by the referees before a decision is rendered.
     44
     45You have 90 days from the date of this email to submit your revision. If you are unable to complete the revision within this time, please contact me to request an extension.
     46
     47You can upload your revised manuscript and submit it through your Author Center. Log into https://mc.manuscriptcentral.com/spe  and enter your Author Center, where you will find your manuscript title listed under "Manuscripts with Decisions".
     48
     49When submitting your revised manuscript, you will be able to respond to the comments made by the referee(s) in the space provided.  You can use this space to document any changes you make to the original manuscript.
     50
     51If you feel that your paper could benefit from English language polishing, you may wish to consider having your paper professionally edited for English language by a service such as Wiley's at http://wileyeditingservices.com. Please note that while this service will greatly improve the readability of your paper, it does not guarantee acceptance of your paper by the journal.
     52 
     53Once again, thank you for submitting your manuscript to Software: Practice and Experience and I look forward to receiving your revision.
     54
     55
     56Sincerely,
     57
     58Prof. Richard Jones
     59Software: Practice and Experience
     60R.E.Jones@kent.ac.uk
     61
     62
     63Referee(s)' Comments to Author:
     64
     65Reviewing: 1
     66
     67Comments to the Author
     68This article presents the design and rationale behind the various
     69threading and synchronization mechanisms of C-forall, a new low-level
     70programming language.  This paper is very similar to a companion paper
     71which I have also received: as the papers are similar, so will these
     72reviews be --- in particular any general comments from the other
     73review apply to this paper also.
     74
     75As far as I can tell, the article contains three main ideas: an
     76asynchronous execution / threading model; a model for monitors to
     77provide mutual exclusion; and an implementation.  The first two ideas
     78are drawn together in Table 1: unfortunately this is on page 25 of 30
     79pages of text. Implementation choices and descriptions are scattered
     80throughout the paper - and the sectioning of the paper seems almost
     81arbitrary.
     82
     83The article is about its contributions.  Simply adding feature X to
     84language Y isn't by itself a contribution, (when feature X isn't
     85already a contribution).  The contribution can be in the design: the
     86motivation, the space of potential design options, the particular
     87design chosen and the rationale for that choice, or the resulting
     88performance.  For example: why support two kinds of generators as well
     89as user-level threads?  Why support both low and high level
     90synchronization constructs?  Similarly I would have found the article
     91easier to follow if it was written top down, presenting the design
     92principles, present the space of language features, justify chosen
     93language features (and rationale) and those excluded, and then present
     94implementation, and performance.
     95
     96Then the writing of the article is often hard to follow, to say the
     97least. Two examples: section 3 "stateful functions" - I've some idea
     98what that is (a function with Algol's "own" or C's "static" variables?
     99but in fact the paper has a rather more specific idea than that. The
     100top of page 3 throws a whole lot of defintions at the reader
     101"generator" "coroutine" "stackful" "stackless" "symmetric"
     102"asymmetric" without every stopping to define each one --- but then in
     103footnote "C" takes the time to explain what C's "main" function is?  I
     104cannot imagine a reader of this paper who doesn't know what "main" is
     105in C; especially if they understand the other concepts already
     106presented in the paper.  The start of section 3 then does the same
     107thing: putting up a whole lot of definitions, making distinctions and
     108comparisons, even talking about some runtime details, but the critical
     109definition of a monitor doesn't appear until three pages later, at the
     110start of section 5 on p15, lines 29-34 are a good, clear, description
     111of what a monitor actually is.  That needs to come first, rather than
     112being buried again after two sections of comparisons, discussions,
     113implementations, and options that are ungrounded because they haven't
     114told the reader what they are actually talking about.  First tell the
     115reader what something is, then how they might use it (as programmers:
     116what are the rules and restrictions) and only then start comparison
     117with other things, other approaches, other languages, or
     118implementations.
     119
     120The description of the implementation is similarly lost in the trees
     121without ever really seeing the wood. Figure 19 is crucial here, but
     122it's pretty much at the end of the paper, and comments about
     123implementations are threaded throughout the paper without the context
     124(fig 19) to understand what's going on.   The protocol for performance
     125testing may just about suffice for C (although is N constantly ten
     126million, or does it vary for each benchmark) but such evaluation isn't
     127appropriate for garbage-collected or JITTed languages like Java or Go.
     128
     129other comments working through the paper - these are mostly low level
     130and are certainly not comprehensive.
     131
     132p1 only a subset of C-forall extensions?
     133
     134p1 "has features often associated with object-oriented programming
     135languages, such as constructors, destructors, virtuals and simple
     136inheritance."   There's no need to quibble about this. Once a language
     137has inheritance, it's hard to claim it's not object-oriented.
     138
     139
     140p2 barging? signals-as-hints?
     141
     142p3 start your discussion of generations with a simple example of a
     143C-forall generator.  Fig 1(b) might do: but put it inline instead of
     144the python example - and explain the key rules and restrictions on the
     145construct.  Then don't even start to compare with coroutines until
     146you've presented, described and explained your coroutines...
     147p3 I'd probably leave out the various "C" versions unless there are
     148key points to make you can't make in C-forall. All the alternatives
     149are just confusing.
     150
     151
     152p4 but what's that "with" in Fig 1(B)
     153
     154p5 start with the high level features of C-forall generators...
     155
     156p5 why is the paper explaining networking protocols?
     157
     158p7 lines 1-9 (transforming generator to coroutine - why would I do any
     159of this? Why would I want one instead of the other (do not use "stack"
     160in your answer!)
     161
     162p10 last para "A coroutine must retain its last resumer to suspend
     163back because the resumer is on a different stack. These reverse
     164pointers allow suspend to cycle backwards, "  I've no idea what is
     165going on here?  why should I care?  Shouldn't I just be using threads
     166instead?  why not?
     167
     168p16 for the same reasons - what reasons?
     169
     170p17 if the multiple-monitor entry procedure really is novel, write a
     171paper about that, and only about that.
     172
     173p23 "Loose Object Definitions" - no idea what that means.  in that
     174section: you can't leave out JS-style dynamic properties.  Even in
     175OOLs that (one way or another) allow separate definitions of methods
     176(like Objective-C, Swift, Ruby, C#) at any time a runtime class has a
     177fixed definition.  Quite why the detail about bit mask implementation
     178is here anyway, I've no idea.
     179
     180p25 this cluster isn't a CLU cluster then?
     181
     182* conclusion should conclude the paper, not the related.
     183
     184
     185Reviewing: 2
     186
     187Comments to the Author
     188This paper describes the concurrency features of an extension of C (whose name I will write as "C\/" here, for convenience), including much design-level discussion of the coroutine- and monitor-based features and some microbenchmarks exploring the current implementation's performance. The key message of the latter is that the system's concurrency abstractions are much lighter-weight than the threading found in mainstream C or Java implementations.
     189
     190There is much description of the system and its details, but nothing about (non-artificial) uses of it. Although the microbenchmark data is encouraging, arguably not enough practical experience with the system has been reported here to say much about either its usability advantages or its performance.
     191
     192As such, the main contribution of the paper seem to be to document the existence of the described system and to provide a detailed design rationale and (partial) tutorial. I believe that could be of interest to some readers, so an acceptable manuscript is lurking in here somewhere.
     193
     194Unfortunately, at present the writing style is somewhere between unclear and infuriating. It omits to define terms; it uses needlessly many terms for what are apparently (but not clearly) the same things; it interrupts itself rather than deliver the natural consequent of whatever it has just said; and so on. Section 5 is particularly bad in these regards -- see my detailed comments below. Fairly major additional efforts will be needed to turn the present text into a digestible design-and-tutorial document. I suspect that a shorter paper could do this job better than the present manuscript, which is overwrought in parts.
     195
     196p2: lines 4--9 are a little sloppy. It is not the languages but their popular implementations which "adopt" the 1:1 kernel threading model.
     197
     198line 10: "medium work" -- "medium-sized work"?
     199
     200line 18: "is all sequential to the compiler" -- not true in modern compilers, and in 2004 H-J Boehm wrote a tech report describing exactly why ("Threads cannot be implemented as a library", HP Labs).
     201
     202line 20: "knows the optimization boundaries" -- I found this vague. What's an example?
     203
     204line 31: this paragraph has made a lot of claims. Perhaps forward-reference to the parts of the paper that discuss each one.
     205
     206line 33: "so the reader can judge if" -- this reads rather passive-aggressively. Perhaps better: "... to support our argument that..."
     207
     208line 41: "a dynamic partitioning mechanism" -- I couldn't tell what this meant
     209
     210p3. Presenting concept of a "stateful function" as a new language feature seems odd. In C, functions often have local state thanks to static local variables (or globals, indeed). Of course, that has several limitations. Can you perhaps present your contributions by enumerating these limitations? See also my suggestion below about a possible framing centred on a strawman.
     211
     212line 2: "an old idea that is new again" -- this is too oblique
     213
     214lines 2--15: I found this to be a word/concept soup. Stacks, closures, generators, stackless stackful, coroutine, symmetric, asymmetric, resume/suspend versus resume/resume... there needs to be a more gradual and structured way to introduce all this, and ideally one that minimises redundancy. Maybe present it as a series of "definitions" each with its own heading, e.g. "A closure is stackless if its local state has statically known fixed size"; "A generator simply means a stackless closure." And so on. Perhaps also strongly introduce the word "activate" as a direct contrast with resume and suspend. These are just a flavour of the sort of changes that might make this paragraph into something readable.
     215
     216Continuing the thought: I found it confusing that by these definitinos, a stackful closure is not a stack, even though logically the stack *is* a kind of closure (it is a representation of the current thread's continuation).
     217
     218lines 24--27: without explaining what the boost functor types mean, I don't think the point here comes across.
     219
     220line 34: "semantically coupled" -- I wasn't surew hat this meant
     221
     222p4: the point of Figure 1 (C) was not immediately clear. It seem to be showing how one might "compile down" Figure 1 (B). Or is that Figure 1 (A)?
     223
     224It's right that the incidental language features of the system are not front-and-centre, but I'd appreciate some brief glossing of non-C languages features as they appear. Examples are the square bracket notation, the pipe notation and the constructor syntax. These explanations could go in the caption of the figure which first uses them, perhaps. Overall I found the figure captions to be terse, and a missed opportunity to explain clearly what was going on.
     225
     226p5 line 23: "This restriction is removed..." -- give us some up-front summary of your contributions and the elements of the language design that will be talked about, so that this isn't an aside. This will reduce the "twisty passages" feeling that characterises much of the paper.
     227
     228line 40: "a killer asymmetric generator" -- this is stylistically odd, and the sentence about failures doesn't convincigly argue that C\/ will help with them. Have you any experience writing device drivers using C\/? Or any argument that the kinds of failures can be traced to the "stack-ripping" style that one is forced to use without coroutines? Also, a typo on line 41: "device drives". And saying "Windows/Linux" is sloppy... what does the cited paper actually say?
     229
     230p6 lines 13--23: this paragraph is difficult to understand. It seems to be talking about a control-flow pattern roughly equivalent to tail recursion. What is the high-level point, other than that this is possible?
     231
     232line 34: "which they call coroutines" -- a better way to make this point is presumably that the C++20 proposal only provides a specialised kind of coroutine, namely generators, despite its use of the more general word.
     233
     234line 47: "... due to dynamic stack allocation, execution..." -- this sentence doesn't scan. I suggest adding "and for" in the relevant places where currently there are only commas.
     235
     236p8 / Figure 5 (B) -- the GNU C extension of unary "&&" needs to be explained. The whole figure needs a better explanation, in fact.
     237
     238p9, lines 1--10: I wasn't sure this stepping-through really added much value. What are the truly important points to note about this code?
     239
     240p10: similarly, lines 3--27 again are somewhere between tedious and confusing. I'm sure the motivation and details of "starter semantics" can both be stated much more pithily.
     241
     242line 32: "a self-resume does not overwrite the last resumer" -- is this a hack or a defensible principled decision?
     243
     244p11: "a common source of errors" -- among beginners or among production code? Presumably the former.
     245
     246line 23: "with builtin and library" -- not sure what this means
     247
     248lines 31--36: these can be much briefer. The only important point here seems to be that coroutines cannot be copied.
     249
     250p12: line 1: what is a "task"? Does it matter?
     251
     252line 7: calling it "heap stack" seems to be a recipe for confusion. "Stack-and-heap" might be better, and contrast with "stack-and-VLS" perhaps. When "VLS" is glossed, suggest actually expanding its initials: say "length" not "size".
     253
     254line 21: are you saying "cooperative threading" is the same as "non-preemptive scheduling", or that one is a special case (kind) of the other? Both are defensible, but be clear.
     255
     256line 27: "mutual exclusion and synchronization" -- the former is a kind of the latter, so I suggest "and other forms of synchronization".
     257
     258line 30: "can either be a stackless or stackful" -- stray "a", but also, this seems to be switching from generic/background terminology to C\/-specific terminology.
     259
     260An expositional idea occurs: start the paper with a strawman naive/limited realisation of coroutines -- say, Simon Tatham's popular "Coroutines in C" web page -- and identify point by point what the limitations are and how C\/ overcomes them. Currently the presentation is often flat (lacking motivating contrasts) and backwards (stating solutions before problems). The foregoing approach might fix both of these.
     261
     262page 13: line 23: it seems a distraction to mention the Python feature here.
     263
     264p14 line 5: it seems odd to describe these as "stateless" just because they lack shared mutable state. It means the code itself is even more stateful. Maybe the "stack ripping" argument could usefully be given here.
     265
     266line 16: "too restrictive" -- would be good to have a reference to justify this, or at least give a sense of what the state-of-the-art performance in transactional memory systems is (both software and hardware)
     267
     268line 22: "simulate monitors" -- what about just *implementing* monitors? isn't that what these systems do? or is the point more about refining them somehow into something more specialised?
     269
     270p15: sections 4.1 and 4.2 seem adrift and misplaced. Split them into basic parts (which go earlier) and more advanced parts (e.g. barging, which can be explained later).
     271
     272line 31: "acquire/release" -- misses an opportunity to contrast the monitor's "enter/exit" abstraction with the less structured acquire/release of locks.
     273
     274p16 line 12: the "implicit" versus "explicit" point is unclear. Is it perhaps about the contract between an opt-in *discipline* and a language-enforced *guarantee*?
     275
     276line 28: no need to spend ages dithering about which one is default and which one is the explicit qualifier. Tell us what you decided, briefly justify it, and move on.
     277
     278p17: Figure 11: since the main point seems to be to highlight bulk acquire, include a comment which identifies the line where this is happening.
     279
     280line 2: "impossible to statically..." -- or dynamically. Doing it dynamically would be perfectly acceptable (locking is a dynamic operation after all)
     281
     282"guarantees acquisition order is consistent" -- assuming it's done in a single bulk acquire.
     283
     284p18: section 5.3: the text here is a mess. The explanations of "internal" versus "external" scheduling are unclear, and "signals as hints" is not explained. "... can cause thread starvation" -- means including a while loop, or not doing so? "There are three signalling mechanisms.." but the text does not follow that by telling us what they are. My own scribbled attempt at unpicking the internal/external thing: "threads already in the monitor, albeit waiting, have priority over those trying to enter".
     285
     286p19: line 3: "empty condition" -- explain that condition variables don't store anything. So being "empty" means that the queue of waiting threads (threads waiting to be signalled that the condition has become true) is empty.
     287
     288line 6: "... can be transformed into external scheduling..." -- OK, but give some motivation.
     289
     290p20: line 6: "mechnaism"
     291
     292lines 16--20: this is dense and can probably only be made clear with an example
     293
     294p21 line 21: clarify that nested monitor deadlock was describe earlier (in 5.2). (Is the repetition necessary?)
     295
     296line 27: "locks, and by extension monitors" -- this is true but the "by extension" argument is faulty. It is perfectly possible to use locks as a primitive and build a compositional mechanism out of them, e.g. transactions.
     297
     298p22 line 2: should say "restructured"
     299
     300line 33: "Implementing a fast subset check..." -- make clear that the following section explains how to do this. Restructuring the sections themselves could do this, or noting in the text.
     301
     302p23: line 3: "dynamic member adding, eg, JavaScript" -- needs to say "as permitted in JavaScript", and "dynamically adding members" is stylistically better
     303
     304p23: line 18: "urgent stack" -- back-reference to where this was explained before
     305
     306p24 line 7: I did not understand what was more "direct" about "direct communication". Also, what is a "passive monitor" -- just a monitor, given that monitors are passive by design?
     307
     308line 14 / section 5.9: this table was useful and it (or something like it) could be used much earlier on to set the structure of the rest of the paper. The explanation at present is too brief, e.g. I did not really understand the point about cases 7 and 8.
     309
     310p25 line 2: instead of casually dropping in a terse explanation for the newly intrdouced term "virtual processor", introduce it properly. Presumably the point is to give a less ambiguous meaning to "thread" by reserving it only for C\/'s green threads.
     311
     312Table 1: what does "No / Yes" mean?
     313
     314p26 line 15: "transforms user threads into fibres" -- a reference is needed to explain what "fibres" means... guessing it's in the sense of Adya et al.
     315
     316line 20: "Microsoft runtime" -- means Windows?
     317
     318lines 21--26: don't say "interrupt" to mean "signal", especially not without clear introduction. You can use "POSIX signal" to disambiguate from condition variables' "signal".
     319
     320p27 line 3: "frequency is usually long" -- that's a "time period" or "interval", not a frequency
     321
     322line 5: the lengthy quotation is not really necessary; just paraphrase the first sentence and move on.
     323
     324line 20: "to verify the implementation" -- I don't think that means what is intended
     325
     326Tables in section 7 -- too many significant figures. How many overall runs are described? What is N in each case?
     327
     328p29 line 2: "to eliminate this cost" -- arguably confusing since nowadays on commodity CPUs most of the benefits of inlining are not to do with call overheads, but from later optimizations enabled as a consequence of the inlining
     329
     330line 41: "a hierarchy" -- are they a hierarchy? If so, this could be explained earlier. Also, to say these make up "an integrated set... of control-flow features" verges on the tautologous.
     331
     332p30 line 15: "a common case being web servers and XaaS" -- that's two cases
     333
     334
     335Reviewing: 3
     336
     337Comments to the Author
     338# Cforall review
     339
     340Overall, I quite enjoyed reading the paper. Cforall has some very interesting ideas. I did have some suggestions that I think would be helpful before final publication. I also left notes on various parts of the paper that I find confusing when reading, in hopes that it may be useful to you.
     341
     342## Summary
     343
     344* Expand on the motivations for including both generator and coroutines, vs trying to build one atop the other
     345* Expand on the motivations for having Why both symmetric and asymettric coroutines?
     346* Comparison to async-await model adopted by other languages
     347    * C#, JS
     348    * Rust and its async/await model
     349* Consider performance comparisons against node.js and Rust frameworks
     350* Discuss performance of monitors vs finer-grained memory models and atomic operations found in other languages
     351* Why both internal/external scheduling for synchronization?
     352
     353## Generator/coroutines
     354
     355In general, this section was clear, but I thought it would be useful to provide a somewhat deeper look into why Cforall opted for the particular combination of features that it offers. I see three main differences from other languages:
     356
     357* Generators are not exposed as a "function" that returns a generator object, but rather as a kind of struct, with communication happening via mutable state instead of "return values". That is, the generator must be manually resumed and (if I understood) it is expected to store values that can then later be read (perhaps via methods), instead of having a `yield <Expr>` statement that yields up a value explicitly.
     358* Both "symmetric" and "asymmetric" generators are supported, instead of only asymmetric.
     359* Coroutines (multi-frame generators) are an explicit mechanism.
     360
     361In most other languages, coroutines are rather built by layering single-frame generators atop one another (e.g., using a mechanism like async-await), and symmetric coroutines are basically not supported. I'd like to see a bit more justification for Cforall including all the above mechanisms -- it seemed like symmetric coroutines were a useful building block for some of the user-space threading and custom scheduler mechanisms that were briefly mentioned later in the paper.
     362
     363In the discussion of coroutines, I would have expected a bit more of a comparison to the async-await mechanism offered in other languages. Certainly the semantics of async-await in JavaScript implies significantly more overhead (because each async fn is a distinct heap object). [Rust's approach avoids this overhead][zc], however, and might be worthy of a comparison (see the Performance section).
     364
     365## Locks and threading
     366
     367### Comparison to atomics overlooks performance
     368
     369There are several sections in the paper that compare against atomics -- for example, on page 15, the paper shows a simple monitor that encapsulates an integer and compares that to C++ atomics. Later, the paper compares the simplicity of monitors against the `volatile` quantifier from Java. The conclusion in section 8 also revisits this point.
     370
     371While I agree that monitors are simpler, they are obviously also significantly different from a performance perspective -- the paper doesn't seem to address this at all. It's plausible that (e.g.) the `Aint` monitor type described in the paper can be compiled and mapped to the specialized instructions offered by hardware, but I didn't see any mention of how this would be done. There is also no mention of the more nuanced memory ordering relations offered by C++11 and how one might achieve similar performance characteristics in Cforall (perhaps the answer is that one simply doesn't need to; I think that's defensible, but worth stating explicitly).
     372
     373### Justification for external scheduling feels lacking
     374
     375Cforall includes both internal and external scheduling; I found the explanation for the external scheduling mechanism to be lacking in justification. Why include both mechanisms when most languages seem to make do with only internal scheduling? It would be useful to show some scenarios where external scheduling is truly more powerful.
     376
     377I would have liked to see some more discussion of external scheduling and how it  interacts with software engineering best practices. It seems somewhat similar to AOP in certain regards. It seems to add a bit of "extra semantics" to monitor methods, in that any method may now also become a kind of synchronization point. The "open-ended" nature of this feels like it could easily lead to subtle bugs, particularly when code refactoring occurs (which may e.g. split an existing method into two). This seems particularly true if external scheduling can occur across compilation units -- the paper suggested that this is true, but I wasn't entirely clear.
     378
     379I would have also appreciated a few more details on how external scheduling is implemented. It seems to me that there must be some sort of "hooks" on mutex methods so that they can detect whether some other function is waiting on them and awaken those blocked threads. I'm not sure how such hooks are inserted, particularly across compilation units. The material in Section 5.6 didn't quite clarify the matter for me. For example, it left me somewhat confused about whether the `f` and `g` functions declared were meant to be local to a translation unit, or shared with other unit.
     380
     381### Presentation of monitors is somewhat confusing
     382
     383I found myself confused fairly often in the section on monitors. I'm just going to leave some notes here on places that I got confused in how that it could be useful to you as feedback on writing that might want to be clarified.
     384
     385To start, I did not realize that the `mutex_opt` notation was a keyword, I thought it was a type annotation. I think this could be called out more explicitly.
     386
     387Later, in section 5.2, the paper discusses `nomutex` annotations, which initially threw me, as they had not been introduced (now I realize that this paragraph is there to justify why there is no such keyword). The paragraph might be rearranged to make that clearer, perhaps by leading with the choice that Cforall made.
     388
     389On page 17, the paper states that "acquiring multiple monitors is safe from deadlock", but this could be stated a bit more precisely: acquiring multiple monitors in a bulk-acquire is safe from deadlock (deadlock can still result from nested acquires).
     390
     391On page 18, the paper states that wait states do not have to be enclosed in loops, as there is no concern of barging. This seems true but there are also other reasons to use loops (e.g., if there are multiple reasons to notify on the same condition). Thus the statement initially surprised me, as barging is only one of many reasons that I typically employ loops around waits.
     392
     393I did not understand the diagram in Figure 12 for some time. Initially, I thought that it was generic to all monitors, and I could not understand the state space. It was only later that I realized it was specific to your example. Updating the caption from "Monitor scheduling to "Monitor scheduling in the example from Fig 13" might have helped me quite a bit.
     394
     395I spent quite some time reading the boy/girl dating example (\*) and I admit I found it somewhat confusing. For example, I couldn't tell whether there were supposed to be many "girl" threads executing at once, or if there was only supposed to be one girl and one boy thread executing in a loop. Are the girl/boy threads supposed to invoke the girl/boy methods or vice versa? Surely there is some easier way to set this up? I believe that when reading the paper I convinced myself of how it was supposed to be working, but I'm writing this review some days later, and I find myself confused all over again and not able to easily figure it out.
     396
     397(\*) as an aside, I would consider modifying the example to some other form of matching, like customers and support personnel.
     398
     399## Related work
     400
     401The paper offered a number of comparisons to Go, C#, Scala, and so forth, but seems to have overlooked another recent language, Rust. In many ways, Rust seems to be closest in philosophy to Cforall, so it seems like an odd omission. I already mentioned above that Rust is in the process of shipping [async-await syntax][aa], which is definitely an alternative to the generator/coroutine approach in Cforall (though one with clear pros/cons).
     402
     403## Performance
     404
     405In the performance section in particular, you might consider comparing against some of the Rust web servers and threading systems. For example, actix is top of the [single query TechEmpower Framework benchmarks], and tokio is near the top of the [plainthreading benchmarks][pt] (hyper, the top, is more of an HTTP framework, though it is also written in Rust). It would seem worth trying to compare their "context switching" costs as well -- I believe both actix and tokio have a notion of threads that could be readily compared.
     406
     407Another addition that might be worth considering is to compare against node.js promises, although I think the comparison to process creation is not as clean.
     408
     409That said, I think that the performance comparison is not a big focus of the paper, so it may not be necessary to add anything to it.
     410
     411## Authorship of this review
     412
     413I'm going to sign this review. This review was authored by Nicholas D. Matsakis. In the intrerest of full disclosure, I'm heavily involved in the Rust project, although I dont' think that influenced this review in particular. Feel free to reach out to me for clarifying questions.
     414
     415## Links
     416
     417[aa]: https://blog.rust-lang.org/2019/09/30/Async-await-hits-beta.html
     418[zc]: https://aturon.github.io/blog/2016/08/11/futures/
     419[sq]: https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=db
     420[pt]: https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=plaintext
     421
     422
     423
     424Subject: Re: manuscript SPE-19-0219
     425To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
     426From: Richard Jones <R.E.Jones@kent.ac.uk>
     427Date: Tue, 12 Nov 2019 22:43:55 +0000
     428
     429Dear Dr Buhr
     430
     431Your should have received a decision letter on this today. I am sorry that this
     432has taken so long. Unfortunately SP&E receives a lot of submissions and getting
     433reviewers is a perennial problem.
     434
     435Regards
     436Richard
     437
     438Peter A. Buhr wrote on 11/11/2019 13:10:
     439>     26-Jun-2019
     440>     Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall"
     441>     has been received by Software: Practice and Experience. It will be given
     442>     full consideration for publication in the journal.
     443>
     444> Hi, it has been over 4 months since submission of our manuscript SPE-19-0219
     445> with no response.
     446>
     447> Currently, I am refereeing a paper for IEEE that already cites our prior SP&E
     448> paper and the Master's thesis forming the bases of the SP&E paper under
     449> review. Hence our work is apropos and we want to get it disseminates as soon as
     450> possible.
     451>
     452> [3] A. Moss, R. Schluntz, and P. A. Buhr, "Cforall: Adding modern programming
     453>      language features to C," Software - Practice and Experience, vol. 48,
     454>      no. 12, pp. 2111-2146, 2018.
     455>
     456> [4] T. Delisle, "Concurrency in C for all," Master's thesis, University of
     457>      Waterloo, 2018.  [Online].  Available:
     458>      https://uwspace.uwaterloo.ca/bitstream/handle/10012/12888
     459
     460
     461
     462Date: Mon, 13 Jan 2020 05:33:15 +0000
     463From: Richard Jones <onbehalfof@manuscriptcentral.com>
     464Reply-To: R.E.Jones@kent.ac.uk
     465To: pabuhr@uwaterloo.ca
     466Subject: Revision reminder - SPE-19-0219
     467
     46813-Jan-2020
     469Dear Dr Buhr
     470SPE-19-0219
     471
     472This is a reminder that your opportunity to revise and re-submit your
     473manuscript will expire 28 days from now. If you require more time please
     474contact me directly and I may grant an extension to this deadline, otherwise
     475the option to submit a revision online, will not be available.
     476
     477I look forward to receiving your revision.
     478
     479Sincerely,
     480
     481Prof. Richard Jones
     482Editor, Software: Practice and Experience
     483https://mc.manuscriptcentral.com/spe
     484
     485
     486
     487Date: Wed, 5 Feb 2020 04:22:18 +0000
     488From: Aaron Thomas <onbehalfof@manuscriptcentral.com>
     489Reply-To: speoffice@wiley.com
     490To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca
     491Subject: SPE-19-0219.R1 successfully submitted
     492
     49304-Feb-2020
     494
     495Dear Dr Buhr,
     496
     497Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" has
     498been successfully submitted online and is presently being given full
     499consideration for publication in Software: Practice and Experience.
     500
     501Your manuscript number is SPE-19-0219.R1.  Please mention this number in all
     502future correspondence regarding this submission.
     503
     504You can view the status of your manuscript at any time by checking your Author
     505Center after logging into https://mc.manuscriptcentral.com/spe.  If you have
     506difficulty using this site, please click the 'Get Help Now' link at the top
     507right corner of the site.
     508
     509Thank you for submitting your manuscript to Software: Practice and Experience.
     510
     511Sincerely,
     512Software: Practice and Experience Editorial Office
     513
     514
     515
     516Date: Sat, 18 Apr 2020 10:42:13 +0000
     517From: Richard Jones <onbehalfof@manuscriptcentral.com>
     518Reply-To: R.E.Jones@kent.ac.uk
     519To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca
     520Subject: Software: Practice and Experience - Decision on Manuscript ID
     521 SPE-19-0219.R1
     522
     52318-Apr-2020
     524
     525Dear Dr Buhr,
     526
     527Many thanks for submitting SPE-19-0219.R1 entitled "Advanced Control-flow and Concurrency in Cforall" to Software: Practice and Experience. The paper has now been reviewed and the comments of the referees are included at the bottom of this letter.
     528
     529I believe that we are making progress here towards a paper that can be published in Software: Practice and Experience.  However the referees still have significant concerns about the paper. The journal's focus is on practice and experience, and one of the the reviewers' concerns remains that your submission should focus the narrative more on the perspective of the programmer than the language designer. I agree that this would strengthen your submission, and I ask you to address this as well as the referees' other comments.
     530
     531A revised version of your manuscript that takes into account the comments of the referee(s) will be reconsidered for publication.
     532
     533Please note that submitting a revision of your manuscript does not guarantee eventual acceptance, and that your revision may be subject to re-review by the referees before a decision is rendered.
     534
     535You have 90 days from the date of this email to submit your revision. If you are unable to complete the revision within this time, please contact me to request a short extension.
     536
     537You can upload your revised manuscript and submit it through your Author Center. Log into https://mc.manuscriptcentral.com/spe  and enter your Author Center, where you will find your manuscript title listed under "Manuscripts with Decisions".
     538
     539When submitting your revised manuscript, you will be able to respond to the comments made by the referee(s) in the space provided.  You can use this space to document any changes you make to the original manuscript.
     540
     541If you would like help with English language editing, or other article preparation support, Wiley Editing Services offers expert help with English Language Editing, as well as translation, manuscript formatting, and figure formatting at www.wileyauthors.com/eeo/preparation. You can also check out our resources for Preparing Your Article for general guidance about writing and preparing your manuscript at www.wileyauthors.com/eeo/prepresources.
     542
     543Once again, thank you for submitting your manuscript to Software: Practice and Experience and I look forward to receiving your revision.
     544
     545Sincerely,
     546Richard
     547
     548Prof. Richard Jones
     549Software: Practice and Experience
     550R.E.Jones@kent.ac.uk
     551
     552
     553Referee(s)' Comments to Author:
     554
     555Reviewing: 1
     556
     557Comments to the Author
     558(A relatively short second review)
     559
     560I thank the authors for their revisions and comprehensive response to
     561reviewers' comments --- many of my comments have been successfully
     562addressed by the revisions.  Here I'll structure my comments around
     563the main salient points in that response which I consider would
     564benefit from further explanation.
     565
     566>  Table 1 is moved to the start and explained in detail.
     567
     568I consider this change makes a significant improvement to the paper,
     569laying out the landscape of language features at the start, and thus
     570addresses my main concerns about the paper.
     571
     572I still have a couple of issues --- perhaps the largest is that it's
     573still not clear at this point in the paper what some of these options
     574are, or crucially how they would be used. I don't know if it's
     575possbile to give high-level examples or use cases to be clear about
     576these up front - or if that would duplicate too much information from
     577later in the paper - either way expanding out the discussion - even if
     578just two a couple of sentences for each row - would help me more.  The
     579point is not just to define these categories but to ensure the
     580readers' understanding of these definitons agrees with that used in
     581the paper.
     582
     583in a little more detail:
     584
     585 * 1st para section 2 begs the question: why not support each
     586   dimension independently, and let the programmer or library designer
     587   combiine features?
     588
     589 * "execution state" seems a relatively low-level description here.
     590  I don't think of e.g. the lambda calculus that way. Perhaps it's as
     591  good a term as any.
     592
     593 * Why must there "be language mechanisms to create, block/unblock,
     594   and join with a thread"?  There aren't in Smalltalk (although there
     595   are in the runtime).  Especially given in Cforall those mechanisms
     596   are *implicit* on thread creation and destruction?
     597
     598 * "Case 1 is a function that borrows storage for its state (stack
     599   frame/activation) and a thread from its invoker"
     600
     601   this much makes perfect sense to me, but I don't understand how a
     602   non-stateful, non-theaded function can then retain
     603
     604   "this state across callees, ie, function local-variables are
     605   retained on the stack across calls."
     606
     607   how can it retain function-local values *across calls* when it
     608   doesn't have any functional-local state?
     609
     610   I'm not sure if I see two separate cases here - rougly equivalent
     611   to C functions without static storage, and then C functions *with*
     612   static storage. I assumed that was the distinction between cases 1
     613   & 3; but perhpas the actual distinction is that 3 has a
     614   suspend/resume point, and so the "state" in figure 1 is this
     615   component of execution state (viz figs 1 & 2), not the state
     616   representing the cross-call variables?
     617
     618>    but such evaluation isn't appropriate for garbage-collected or JITTed
     619   languages like Java or Go.
     620
     621For JITTed languages in particular, reporting peak performance needs
     622to "warm up" the JIT with a number of iterators before beginning
     623measurement. Actually for JIT's its even worse: see Edd Barrett et al
     624OOPSLA 2017.
     625   
     626
     627
     628minor issues:
     629
     630 * footnote A - I've looked at various other papers & the website to
     631   try to understand how "object-oriented" Cforall is - I'm still not
     632   sure.  This footnote says Cforall has "virtuals" - presumably
     633   virtual functions, i.e. dynamic dispatch - and inheritance: that
     634   really is OO as far as I (and most OO people) are concerned.  For
     635   example Haskell doesn't have inheritance, so it's not OO; while
     636   CLOS (the Common Lisp *Object* System) or things like Cecil and
     637   Dylan are considered OO even though they have "multiple function
     638   parameters as receivers", lack "lexical binding between a structure
     639   and set of functions", and don't have explicit receiver invocation
     640   syntax.  Python has receiver syntax, but unlike Java or Smalltalk
     641   or C++, method declarations still need to have an explicit "self"
     642   receiver parameter.  Seems to me that Go, for example, is
     643   more-or-less OO with interfaces, methods, and dynamic dispatch (yes
     644   also and an explicit receiver syntax but that's not
     645   determiniative); while Rust lacks dynamic dispatch built-in.  C is
     646   not OO as a language, but as you say given it supports function
     647   pointers with structures, it does support an OO programm style.
     648 
     649   This is why I again recommend just not buying into this fight: not
     650   making any claims about whether Cforall is OO or is not - because
     651   as I see it, the rest of the paper doesn't depend on whether
     652   Cforall is OO or not.  That said: this is just a recommendation,
     653   and I won't quibble over this any further.
     654
     655 * is a "monitor function" the same as a "mutex function"?
     656   if so the paper should pick one term; if not, make the distinction clear.
     657
     658
     659 * "As stated on line 1 because state declarations from the generator
     660    type can be moved out of the coroutine type into the coroutine main"
     661
     662    OK sure, but again: *why* would a programmer want to do that?
     663    (Other than, I guess, to show the difference between coroutines &
     664    generators?)  Perhaps another way to put this is that the first
     665    para of 3.2 gives the disadvantages of coroutines vs-a-vs
     666    generators, briefly describes the extended semantics, but never
     667    actualy says why a programmer may want those extended semantics,
     668    or how they would benefit.  I don't mean to belabour the point,
     669    but (generalist?) readers like me would generally benefit from
     670    those kinds of discussions about each feature throughout the
     671    paper: why might a programmer want to use them?
     672   
     673
     674> p17 if the multiple-monitor entry procedure really is novel, write a paper
     675> about that, and only about that.
     676
     677> We do not believe this is a practical suggestion.
     678
     679 * I'm honestly not trying to be snide here: I'm not an expert on
     680   monitor or concurrent implementations. Brinch Hansen's original
     681   monitors were single acquire; this draft does not cite any other
     682   previous work that I could see. I'm not suggesting that the brief
     683   mention of this mechanism necessarily be removed from this paper,
     684   but if this is novel (and a clear advance over a classical OO
     685   monitor a-la Java which only acquires the distinguished reciever)
     686   then that would be worth another paper in itself.
     687 
     688> * conclusion should conclude the paper, not the related.
     689> We do not understand this comment.if ithis
     690
     691My typo: the paper's conclusion should come at the end, after the
     692future work section.
     693
     694
     695
     696
     697To encourage accountability, I'm signing my reviews in 2020.
     698For the record, I am James Noble, kjx@ecs.vuw.ac.nz.
     699
     700
     701Reviewing: 2
     702
     703Comments to the Author
     704I thank the authors for their detailed response. To respond to a couple of points raised  in response to my review (number 2):
     705
     706- on the Boehm paper and whether code is "all sequential to the compiler": I now understand the authors' position better and suspect we are in violent agreement, except for whether it's appropriate to use the rather breezy phrase "all sequential to the compiler". It would be straightforward to clarify that code not using the atomics features is optimized *as if* it were sequential, i.e. on the assumption of a lack of data races.
     707
     708- on the distinction between "mutual exclusion" and "synchronization": the added citation does help, in that it makes a coherent case for the definition the authors prefer. However, the text could usefully clarify that this is a matter of definition not of fact, given especially that in my assessment the authors' preferred definition is not the most common one. (Although the mention of Hoare's apparent use of this definition is one data point, countervailing ones are found in many contemporaneous or later papers, e.g. Habermann's 1972 "Synchronization of Communicating Processes" (CACM 15(3)), Reed & Kanodia's 1979 "Synchronization with eventcounts and sequencers" (CACM (22(2)) and so on.)
     709
     710I am glad to see that the authors have taken on board most of the straightforward improvements I suggested.
     711
     712However, a recurring problem of unclear writing still remains through many parts of the paper, including much of sections 2, 3 and 6. To highlight a couple of problem patches (by no means exhaustive):
     713
     714- section 2 (an expanded version of what was previously section 5.9) lacks examples and is generally obscure and allusory ("the most advanced feature" -- name it! "in triplets" -- there is only one triplet!; what are "execution locations"? "initialize" and "de-initialize" what? "borrowed from the invoker" is a concept in need of explaining or at least a fully explained example -- in what sense does a plain function borrow" its stack frame? "computation only" as opposed to what? in 2.2, in what way is a "request" fundamental to "synchronization"? and the "implicitly" versus "explicitly" point needs stating as elsewhere, with a concrete example e.g. Java built-in mutexes versus java.util.concurrent).
     715
     716- section 6: 6.2 omits the most important facts in preference for otherwise inscrutable detail: "identify the kind of parameter" (first say *that there are* kinds of parameter, and what "kinds" means!); "mutex parameters are documentation" is misleading (they are also semantically significant!) and fails to say *what* they mean; the most important thing is surely that 'mutex' is a language feature for performing lock/unlock operations at function entry/exit. So say it! The meanings of examples f3 and f4 remain unclear. Meanwhile in 6.3, "urgent" is not introduced (we are supposed to infer its meaning from Figure 12, but that Figure is incomprehensible to me), and we are told of "external scheduling"'s long history in Ada but not clearly what it actually means; 6.4's description of "waitfor" tells us it is different from an if-else chain but tries to use two *different* inputs to tell us that the behavior is different; tell us an instance where *the same* values of C1 and C2 give different behavior (I even wrote out a truth table and still don't see the semantic difference)
     717
     718The authors frequently use bracketed phrases, and sometimes slashes "/", in ways that are confusing and/or detrimental to readability. Page 13 line 2's "forward (backward)" is one particularly egregious example. In general I would recommend the the authors try to limit their use of parentheses and slashes as a means of forcing a clearer wording to emerge. Also, the use of "eg." is often cursory and does not explain the examples given, which are frequently a one- or two-word phrase of unclear referent.
     719
     720Considering the revision more broadly, none of the more extensive or creative rewrites I suggested in my previous review have been attempted, nor any equivalent efforts to improve its readability. The hoisting of the former section 5.9 is a good idea, but the newly added material accompanying it (around Table 1) suffers fresh deficiencies in clarity. Overall the paper is longer than before, even though (as my previous review stated), I believe a shorter paper is required in order to serve the likely purpose of publication. (Indeed, the authors' letter implies that a key goal of publication is to build community and gain external users.)
     721
     722Given this trajectory, I no longer see a path to an acceptable revision of the present submission. Instead I suggest the authors consider splitting the paper in two: one half about coroutines and stack management, the other about mutexes, monitors and the runtime. (A briefer presentation of the runtime may be helpful in the first paper also, and a brief recap of the generator and coroutine support is obviously needed in the second too.) Both of these new papers would need to be written with a strong emphasis on clarity, paying great care to issues of structure, wording, choices of example, and restraint (saying what's important, not everything that could be said). I am confident the authors could benefit from getting early feedback from others at their institution. For the performance experiments, of course these do not split evenly -- most (but not all) belong in the second of these two hypothetical papers. But the first of them would still have plenty of meat to it; for me, a clear and thorough study of the design space around coroutines is the most interesting and tantalizing prospect.
     723
     724I do not buy the authors' defense of the limited practical experience or "non-micro" benchmarking presented. Yes, gaining external users is hard and I am sympathetic on that point. But building something at least *somewhat* substantial with your own system should be within reach, and without it the "practice and experience" aspects of the work have not been explored. Clearly C\/ is the product of a lot of work over an extended period, so it is a surprise that no such experience is readily available for inclusion.
     725
     726Some smaller points:
     727
     728It does not seem right to state that a stack is essential to Von Neumann architectures -- since the earliest Von Neumann machines (and indeed early Fortran) did not use one.
     729
     730To elaborate on something another reviewer commented on: it is a surprise to find a "Future work" section *after* the "Conclusion" section. A "Conclusions and future work" section often works well.
     731
     732
     733Reviewing: 3
     734
     735Comments to the Author
     736This is the second round of reviewing.
     737
     738As in the first review, I found that the paper (and Cforall) contains
     739a lot of really interesting ideas, but it remains really difficult to
     740have a good sense of which idea I should use and when. This applies in
     741different ways to different features from the language:
     742
     743* coroutines/generators/threads: here there is
     744  some discussion, but it can be improved.
     745* interal/external scheduling: I didn't find any direct comparison
     746  between these features, except by way of example.
     747
     748I requested similar things in my previous review and I see that
     749content was added in response to those requests. Unfortunately, I'm
     750not sure that I can say it improved the paper's overall read. I think
     751in some sense the additions were "too much" -- I would have preferred
     752something more like a table or a few paragraphs highlighting the key
     753reasons one would pick one construct or the other.
     754
     755In general, I do wonder if the paper is just trying to do too much.
     756The discussion of clusters and pre-emption in particular feels quite
     757rushed.
     758
     759## Summary
     760
     761I make a number of suggestions below but the two most important
     762I think are:
     763
     764* Recommend to shorten the comparison on coroutine/generator/threads
     765  in Section 2 to a paragraph with a few examples, or possibly a table
     766  explaining the trade-offs between the constructs
     767* Recommend to clarify the relationship between internal/external
     768  scheduling -- is one more general but more error-prone or low-level?
     769
     770## Coroutines/generators/threads
     771
     772There is obviously a lot of overlap between these features, and in
     773particular between coroutines and generators. As noted in the previous
     774review, many languages have chosen to offer *only* generators, and to
     775build coroutines by stacks of generators invoking one another.
     776
     777I believe the newly introduced Section 2 of the paper is trying to
     778motivate why each of these constructs exist, but I did not find it
     779effective. It was dense and difficult to understand. I think the
     780problem is that Section 2 seems to be trying to derive "from first
     781principles" why each construct exists, but I think that a more "top
     782down" approach would be easier to understand.
     783
     784In fact, the end of Section 2.1 (on page 5) contains a particular
     785paragraph that embodies this "top down" approach. It starts,
     786"programmers can now answer three basic questions", and thus gives
     787some practical advice for which construct you should use and when. I
     788think giving some examples of specific applications that this
     789paragraph, combined with some examples of cases where each construct
     790was needed, would be a better approach.
     791
     792I don't think this compariosn needs to be very long. It seems clear
     793enough that one would
     794
     795* prefer generators for simple computations that yield up many values,
     796* prefer coroutines for more complex processes that have significant
     797  internal structure,
     798* prefer threads for cases where parallel execution is desired or
     799  needed.
     800
     801I did appreciate the comparison in Section 2.3 between async-await in
     802JS/Java and generators/coroutines. I agree with its premise that those
     803mechanisms are a poor replacement for generators (and, indeed, JS has
     804a distinct generator mechanism, for example, in part for this reason).
     805I believe I may have asked for this in a previous review, but having
     806read it, I wonder if it is really necessary, since those mechanisms
     807are so different in purpose.
     808
     809## Internal vs external scheduling
     810
     811I find the motivation for supporting both internal and external
     812scheduling to be fairly implicit. After several reads through the
     813section, I came to the conclusion that internal scheduling is more
     814expressive than external scheduling, but sometimes less convenient or
     815clear. Is this correct? If not, it'd be useful to clarify where
     816external scheduling is more expressive.
     817
     818The same is true, I think, of the `signal_block` function, which I
     819have not encountered before; it seems like its behavior can be modeled
     820with multiple condition variables, but that's clearly more complex.
     821
     822One question I had about `signal_block`: what happens if one signals
     823but no other thread is waiting? Does it block until some other thread
     824waits? Or is that user error?
     825
     826I would find it very interesting to try and capture some of the
     827properties that make internal vs external scheduling the better
     828choice.
     829
     830For example, it seems to me that external scheduling works well if
     831there are only a few "key" operations, but that internal scheduling
     832might be better otherwise, simply because it would be useful to have
     833the ability to name a signal that can be referenced by many
     834methods. Consider the bounded buffer from Figure 13: if it had
     835multiple methods for removing elements, and not just `remove`, then
     836the `waitfor(remove)` call in `insert` might not be sufficient.
     837
     838## Comparison of external scheduling to messaging
     839
     840I did enjoy the section comparing external scheduling to Go's
     841messaging mechanism, which I believe is a new addition.
     842
     843I believe that one difference between the Go program and the Cforall
     844equivalent is that the Goroutine has an associated queue, so that
     845multiple messages could be enqueued, whereas the Cforall equivalent is
     846effectively a "bounded buffer" of length 1. Is that correct? I think
     847this should be stated explicitly. (Presumably, one could modify the
     848Cforall program to include an explicit vector of queued messages if
     849desired, but you would also be reimplementing the channel
     850abstraction.)
     851
     852Also, in Figure 20, I believe that there is a missing `mutex` keyword.
     853The fiugre states:
     854
     855```
     856void main(GoRtn & gortn) with(gortn) {
     857```
     858
     859but I think it should probably be as follows:
     860
     861```
     862void main(GoRtn & mutex gortn) with(gortn) {
     863```
     864
     865Unless there is some implicit `mutex` associated with being a main
     866function for a `monitor thread`.
     867
     868## Atomic operations and race freedom
     869
     870I was glad to see that the paper acknowledged that Cforall still had
     871low-level atomic operations, even if their use is discouraged in favor
     872of higher-level alternatives.
     873
     874However, I still feel that the conclusion overstates the value of the
     875contribution here when it says that "Cforall high-level race-free
     876monitors and threads provide the core mechanisms for mutual exclusion
     877and synchronization, without the need for volatile and atomics". I
     878feel confident that Java programmers, for example, would be advised to
     879stick with synchronized methods whenever possible, and it seems to me
     880that they offer similar advantages -- but they sometimes wind up using
     881volatiles for performance reasons.
     882
     883I was also confused by the term "race-free" in that sentence. In
     884particular, I don't think that Cforall has any mechanisms for
     885preventing *data races*, and it clearly doesn't prevent "race
     886conditions" (which would bar all sorts of useful programs). I suppose
     887that "race free" here might be referring to the improvements such as
     888removing barging behavior.
     889
     890## Performance comparisons
     891
     892In my previous review, I requested comparisons against Rust and
     893node.js, and I see that the new version of the paper includes both,
     894which is a good addition.
     895
     896One note on the Rust results: I believe that the results are comparing
     897against the threads found in Rust's standard library, which are
     898essentially a shallow wrapper around pthreads, and hence the
     899performance is quite close to pthread performance (as one would
     900expect). It would perhaps be more interesting to see a comparison
     901built using [tokio] or [async-std], two of the more prominent
     902user-space threading libraries that build on Rust's async-await
     903feature (which operates quite differently than Javascript's
     904async-await, in that it doesn't cause every aync function call to
     905schedule a distinct task).
     906
     907[tokio]: https://tokio.rs/
     908[async-std]: https://async.rs/
     909
     910That said, I am satisfied with the performance results as they are in
     911the current revision.
     912
     913## Minor notes and typos
     914
     915Several figures used the `with` keyword. I deduced that `with(foo)`
     916permits one to write `bar` instead of `foo.bar`. It seems worth
     917introducing. Apologies if this is stated in the paper, if so I missed
     918it.
     919
     920On page 20, section 6.3, "external scheduling and vice versus" should be
     921"external scheduling and vice versa".
     922
     923On page 5, section 2.3, the paper states "we content" but it should be
     924"we contend".
     925
     926Reviewing: Editor
     927
     928A few small comments in addition to those of the referees.
     929
     930Page 1. I don't believe that it s fair to imply that Scala is  "research vehicle" as it is used by major players, Twitter being the most prominent example.
     931
     932Page 15. Must Cforall threads start after construction (e.g. see your example on page 15, line 21)? I can think of examples where it is not desirable that threads start immediately after construction, e.g. a game with N players, each of whom is expensive to create, but all of whom should be started at the same time.
     933
     934Page 18, line 17: is using
     935
     936
     937
     938Date: Tue, 16 Jun 2020 13:45:03 +0000
     939From: Aaron Thomas <onbehalfof@manuscriptcentral.com>
     940Reply-To: speoffice@wiley.com
     941To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca
     942Subject: SPE-19-0219.R2 successfully submitted
     943
     94416-Jun-2020
     945
     946Dear Dr Buhr,
     947
     948Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" has been successfully submitted online and is presently being given full consideration for publication in Software: Practice and Experience.
     949
     950Your manuscript number is SPE-19-0219.R2.  Please mention this number in all future correspondence regarding this submission.
     951
     952You can view the status of your manuscript at any time by checking your Author Center after logging into https://mc.manuscriptcentral.com/spe.  If you have difficulty using this site, please click the 'Get Help Now' link at the top right corner of the site.
     953
     954
     955Thank you for submitting your manuscript to Software: Practice and Experience.
     956
     957Sincerely,
     958
     959Software: Practice and Experience Editorial Office
     960
     961
     962
     963Date: Wed, 2 Sep 2020 20:55:34 +0000
     964From: Richard Jones <onbehalfof@manuscriptcentral.com>
     965Reply-To: R.E.Jones@kent.ac.uk
     966To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca
     967Subject: Software: Practice and Experience - Decision on Manuscript ID
     968 SPE-19-0219.R2
     969
     97002-Sep-2020
     971
     972Dear Dr Buhr,
     973
     974Many thanks for submitting SPE-19-0219.R2 entitled "Advanced Control-flow and Concurrency in Cforall" to Software: Practice and Experience. The paper has now been reviewed and the comments of the referees are included at the bottom of this letter. I apologise for the length of time it has taken to get these.
     975
     976Both reviewers consider this paper to be close to acceptance. However, before I can accept this paper, I would like you address the comments of Reviewer 2, particularly with regard to the description of the adaptation Java harness to deal with warmup. I would expect to see a convincing argument that the computation has reached a steady state. I would also like you to provide the values for N for each benchmark run. This should be very straightforward for you to do. There are a couple of papers on steady state that you may wish to consult (though I am certainly not pushing my own work).
     977
     9781) Barrett, Edd; Bolz-Tereick, Carl Friedrich; Killick, Rebecca; Mount, Sarah and Tratt, Laurence. Virtual Machine Warmup Blows Hot and Cold. OOPSLA 2017. https://doi.org/10.1145/3133876
     979Virtual Machines (VMs) with Just-In-Time (JIT) compilers are traditionally thought to execute programs in two phases: the initial warmup phase determines which parts of a program would most benefit from dynamic compilation, before JIT compiling those parts into machine code; subsequently the program is said to be at a steady state of peak performance. Measurement methodologies almost always discard data collected during the warmup phase such that reported measurements focus entirely on peak performance. We introduce a fully automated statistical approach, based on changepoint analysis, which allows us to determine if a program has reached a steady state and, if so, whether that represents peak performance or not. Using this, we show that even when run in the most controlled of circumstances, small, deterministic, widely studied microbenchmarks often fail to reach a steady state of peak performance on a variety of common VMs. Repeating our experiment on 3 different machines, we found that at most 43.5% of pairs consistently reach a steady state of peak performance.
     980
     9812) Kalibera, Tomas and Jones, Richard. Rigorous Benchmarking in Reasonable Time. ISMM  2013. https://doi.org/10.1145/2555670.2464160
     982Experimental evaluation is key to systems research. Because modern systems are complex and non-deterministic, good experimental methodology demands that researchers account for uncertainty. To obtain valid results, they are expected to run many iterations of benchmarks, invoke virtual machines (VMs) several times, or even rebuild VM or benchmark binaries more than once. All this repetition costs time to complete experiments. Currently, many evaluations give up on sufficient repetition or rigorous statistical methods, or even run benchmarks only in training sizes. The results reported often lack proper variation estimates and, when a small difference between two systems is reported, some are simply unreliable.In contrast, we provide a statistically rigorous methodology for repetition and summarising results that makes efficient use of experimentation time. Time efficiency comes from two key observations. First, a given benchmark on a given platform is typically prone to much less non-determinism than the common worst-case of published corner-case studies. Second, repetition is most needed where most uncertainty arises (whether between builds, between executions or between iterations). We capture experimentation cost with a novel mathematical model, which we use to identify the number of repetitions at each level of an experiment necessary and sufficient to obtain a given level of precision.We present our methodology as a cookbook that guides researchers on the number of repetitions they should run to obtain reliable results. We also show how to present results with an effect size confidence interval. As an example, we show how to use our methodology to conduct throughput experiments with the DaCapo and SPEC CPU benchmarks on three recent platforms.
     983
     984You have 42 days from the date of this email to submit your revision. If you are unable to complete the revision within this time, please contact me to request a short extension.
     985
     986You can upload your revised manuscript and submit it through your Author Center. Log into https://mc.manuscriptcentral.com/spe and enter your Author Center, where you will find your manuscript title listed under "Manuscripts with Decisions".
     987
     988When submitting your revised manuscript, you will be able to respond to the comments made by the referee(s) in the space provided.  You can use this space to document any changes you make to the original manuscript.
     989
     990If you would like help with English language editing, or other article preparation support, Wiley Editing Services offers expert help with English Language Editing, as well as translation, manuscript formatting, and figure formatting at www.wileyauthors.com/eeo/preparation. You can also check out our resources for Preparing Your Article for general guidance about writing and preparing your manuscript at www.wileyauthors.com/eeo/prepresources.
     991 
     992Once again, thank you for submitting your manuscript to Software: Practice and Experience. I look forward to receiving your revision.
     993
     994Sincerely,
     995Richard
     996
     997Prof. Richard Jones
     998Editor, Software: Practice and Experience
     999R.E.Jones@kent.ac.uk
     1000
     1001Referee(s)' Comments to Author:
     1002
     1003Reviewing: 1
     1004
     1005Comments to the Author
     1006Overall, I felt that this draft was an improvement on previous drafts and I don't have further changes to request.
     1007
     1008I appreciated the new language to clarify the relationship of external and internal scheduling, for example, as well as the new measurements of Rust tokio. Also, while I still believe that the choice between thread/generator/coroutine and so forth could be made crisper and clearer, the current draft of Section 2 did seem adequate to me in terms of specifying the considerations that users would have to take into account to make the choice.
     1009
     1010
     1011Reviewing: 2
     1012
     1013Comments to the Author
     1014First: let me apologise for the delay on this review. I'll blame the global pandemic combined with my institution's senior management's counterproductive decisions for taking up most of my time and all of my energy.
     1015
     1016At this point, reading the responses, I think we've been around the course enough times that further iteration is unlikely to really improve the paper any further, so I'm happy to recommend acceptance.    My main comments are that there were some good points in the responses to *all* the reviews and I strongly encourage the authors to incorporate those discursive responses into the final paper so they may benefit readers as well as reviewers.   I agree with the recommendations of reviewer #2 that the paper could usefully be split in to two, which I think I made to a previous revision, but I'm happy to leave that decision to the Editor.
     1017
     1018Finally, the paper needs to describe how the Java harness was adapted to deal with warmup; why the computation has warmed up and reached a steady state - similarly for js and Python. The tables should also give the "N" chosen for each benchmark run.
     1019 
     1020minor points
     1021* don't start sentences with "However"
     1022* most downloaded isn't an "Award"
     1023
     1024
     1025
     1026Date: Thu, 1 Oct 2020 05:34:29 +0000
     1027From: Richard Jones <onbehalfof@manuscriptcentral.com>
     1028Reply-To: R.E.Jones@kent.ac.uk
     1029To: pabuhr@uwaterloo.ca
     1030Subject: Revision reminder - SPE-19-0219.R2
     1031
     103201-Oct-2020
     1033
     1034Dear Dr Buhr
     1035
     1036SPE-19-0219.R2
     1037
     1038This is a reminder that your opportunity to revise and re-submit your manuscript will expire 14 days from now. If you require more time please contact me directly and I may grant an extension to this deadline, otherwise the option to submit a revision online, will not be available.
     1039
     1040If your article is of potential interest to the general public, (which means it must be timely, groundbreaking, interesting and impact on everyday society) then please e-mail ejp@wiley.co.uk explaining the public interest side of the research. Wiley will then investigate the potential for undertaking a global press campaign on the article.
     1041
     1042I look forward to receiving your revision.
     1043
     1044Sincerely,
     1045
     1046Prof. Richard Jones
     1047Editor, Software: Practice and Experience
     1048
     1049https://mc.manuscriptcentral.com/spe
     1050
     1051
     1052
     1053Date: Tue, 6 Oct 2020 15:29:41 +0000
     1054From: Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com>
     1055Reply-To: speoffice@wiley.com
     1056To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca
     1057Subject: SPE-19-0219.R3 successfully submitted
     1058
     105906-Oct-2020
     1060
     1061Dear Dr Buhr,
     1062
     1063Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" has been successfully submitted online and is presently being given full consideration for publication in Software: Practice and Experience.
     1064
     1065Your manuscript number is SPE-19-0219.R3.  Please mention this number in all future correspondence regarding this submission.
     1066
     1067You can view the status of your manuscript at any time by checking your Author Center after logging into https://mc.manuscriptcentral.com/spe.  If you have difficulty using this site, please click the 'Get Help Now' link at the top right corner of the site.
     1068
     1069
     1070Thank you for submitting your manuscript to Software: Practice and Experience.
     1071
     1072Sincerely,
     1073
     1074Software: Practice and Experience Editorial Office
     1075
     1076
     1077
     1078Date: Thu, 15 Oct 2020 13:48:52 +0000
     1079From: Richard Jones <onbehalfof@manuscriptcentral.com>
     1080Reply-To: R.E.Jones@kent.ac.uk
     1081To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca
     1082Subject: Software: Practice and Experience - Decision on Manuscript ID
     1083 SPE-19-0219.R3
     1084
     108515-Oct-2020
     1086
     1087Dear Dr Buhr,
     1088
     1089It is a pleasure to accept your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" in its current form for publication in Software: Practice and Experience. 
     1090
     1091Please note although the manuscript is accepted the files will now be checked to ensure that everything is ready for publication, and you may be contacted if final versions of files for publication are required.
     1092
     1093Your article cannot be published until the publisher has received the appropriate signed license agreement. Within the next few days the corresponding author will receive an email from Wiley's Author Services system which will ask them to log in and will present them with the appropriate license for completion.
     1094
     1095Thank you for your fine contribution.
     1096
     1097Sincerely,
     1098Richard
     1099
     1100Prof. Richard Jones
     1101Editor, Software: Practice and Experience
     1102R.E.Jones@kent.ac.uk
     1103
     1104P.S. - You can help your research get the attention it deserves! Check out Wiley's free Promotion Guide for best-practice recommendations for promoting your work at www.wileyauthors.com/eeo/guide. And learn more about Wiley Editing Services which offers professional video, design, and writing services to create shareable video abstracts, infographics, conference posters, lay summaries, and research news stories for your research at www.wileyauthors.com/eeo/promotion.
     1105
     1106This journal accepts artwork submissions for Cover Images. This is an optional service you can use to help increase article exposure and showcase your research. For more information, including artwork guidelines, pricing, and submission details, please visit the Journal Cover Image page at www.wileyauthors.com/eeo/covers. If you want help creating an image, Wiley Editing Services offers a professional cover image design service that creates eye-catching images, ready to be showcased on the journal cover at www.wileyauthors.com/eeo/design.
     1107
     1108
     1109
     1110Date: Fri, 16 Oct 2020 12:44:42 +0000
     1111From: Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com>
     1112Reply-To: speoffice@wiley.com
     1113To: pabuhr@uwaterloo.ca
     1114Subject: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]
     1115
     111616-Oct-2020
     1117
     1118Dear Dr. Buhr,
     1119
     1120Manuscript id: SPE-19-0219.R3
     1121Manuscript title: Advanced Control-flow and Concurrency in Cforall
     1122
     1123Although your manuscript has been accepted for publication it is now being returned to your author center for you to review and make any final adjustments or corrections prior to production and publication.
     1124
     1125Any special instructions will be listed below:
     11261) Funding Information added in ScholorOne but missing in main document, Kindly add the Funding information in main document.
     11272) Please provide the clean version of the manuscript without any highlights or tracked changes.
     11283) Kindly check and make sure citations for all figures and Tables are present in the main document
     1129
     1130Please now log back into your Scholar One Author Center and click on the "Manuscripts Accepted for First Look" queue. In order to update the submission, click on the "submit updated manuscript" link in the "Actions" column and follow the steps as you would during a manuscript submission process.
     1131
     1132On the File Upload screen please upload the FINAL versions of all the files, including print quality image files. For information about image quality requirements, please refer to the guidelines at https://authorservices.wiley.com/asset/photos/electronic_artwork_guidelines.pdf.
     1133
     1134Instructions for uploading replacement files:
     11351. On the "File Upload" step, click on the "edit" button for the file you wish to replace.
     11362. In the "Upload a later version" section, browse to locate the replacement final version.
     11373. Add any comments concerning the replacement (e.g. "high res image").
     11384. Select whether the new file is a minor or major version (we suggest you select minor version)
     11395. Click upload.
     11406. Click 'Submit' when all the files have been uploaded and you will receive an automated email to say that submission is successful.
     1141
     1142Please submit your updates within the next 7 days to ensure there are no unnecessary delays in production.
     1143
     1144Sincerely,
     1145Software: Practice and Experience Editorial Office
     1146
     1147
     1148
     1149From: SPE Office <speoffice@wiley.com>
     1150To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
     1151Subject: Re: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]
     1152Date: Mon, 19 Oct 2020 17:04:24 +0000
     1153
     1154Dear Dr. Buhr,
     1155
     1156Thank you very much for contacting the Editorial Office.
     1157
     1158I would like to let you know that the files has been found in order and moved to production.
     1159
     1160Plesae let me know for further assistance in this regard.
     1161
     1162Best Regards
     1163
     1164Mayank Roy Chowdhury
     1165Editorial Assistant
     1166Software practice and Experience
     1167________________________________
     1168From: Peter A. Buhr <pabuhr@uwaterloo.ca>
     1169Sent: Sunday, October 18, 2020 2:00 PM
     1170To: SPE Office <speoffice@wiley.com>
     1171Cc: Thierry Delisle <tdelisle@uwaterloo.ca>
     1172Subject: Re: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]
     1173
     1174       This is an external email.
     1175
     1176    Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com> writes:
     1177
     1178    Instructions for uploading replacement files:
     1179    1. On the "File Upload" step, click on the "edit" button for the file you wish to replace.
     1180    2. In the "Upload a later version" section, browse to locate the replacement final version.
     1181    3. Add any comments concerning the replacement (e.g. "high res image").
     1182    4. Select whether the new file is a minor or major version (we suggest you select minor version)
     1183    5. Click upload.
     1184    6. Click 'Submit' when all the files have been uploaded and you will receive an automated email to say that submission is successful.
     1185
     1186There was no "edit" button on the "File Upload" page, so I just upload the
     1187final version of the PDF and source files using the mechanism on the "File
     1188Upload" page and submitted that.
     1189
     1190
     1191
     1192Date: Tue, 20 Oct 2020 13:28:37 +0530
     1193To: "Dr. Peter Buhr" <pabuhr@uwaterloo.ca>
     1194From: jpcms@spi-global.com
     1195Subject: Information: Production Editor Contact Software:Practice and Experience  | Advanced Control-flow and Concurrency in C A
     1196
     1197Dear Dr. Peter Buhr,
     1198
     1199We are in the process of preparing "Advanced Control-flow and Concurrency in C A" for publication. Your production editor, Joel Pacaanas, will support you and your article throughout the process.
     1200
     1201Please get in touch with your Production Editor at SPEproofs@wiley.com;EllaMae.Navor@spi-global.com if you have any questions.
     1202               
     1203Sincerely,
     1204Booking-in Team,
     1205On behalf of Wiley
     1206
     1207Article ID: SPE_2925
     1208Article DOI: 10.1002/SPE.2925
     1209
     1210
     1211
     1212Date: Tue, 20 Oct 2020 10:33:04 +0000
     1213From: <cs-author@wiley.com>
     1214To: <pabuhr@uwaterloo.ca>
     1215Subject: In Production: Your article accepted in Software: Practice and Experience
     1216
     1217Dear Peter Buhr,
     1218
     1219Article ID: SPE2925
     1220Article DOI: 10.1002/spe.2925
     1221Internal Article ID: 16922213
     1222Article: Advanced Control-flow and Concurrency in C A
     1223Journal: Software: Practice and Experience
     1224
     1225Congratulations on the acceptance of your article for publication in Software: Practice and Experience.
     1226
     1227Your article has been received and the production process is now underway. We look forward to working with you and publishing your article. Using Wiley Author Services, you can track your article's progress.
     1228
     1229Please click below to login - if you are using a different email address than this one, you will need to manually assign this article to your Dashboard (see https://hub.wiley.com/docs/support/assigning-a-missing-article-to-my-dashboard-DOC-11871?utm_source=new%20user%20invitation&utm_medium=email How do I assign a missing article to My Dashboard?):
     1230
     1231https://authorservices.wiley.com/index.html#login?campaign=email_invitation-new
     1232
     1233If applicable, a list of available actions will appear below - check out your Author Services Dashboard for all actions related to your articles.
     1234
     1235Sign your license agreement (REQUIRED)  -- you will receive an email when this task is ready on your dashboard. Track your article's progress to publicationAccess your published articleInvite colleagues to view your published article
     1236If you need any assistance, please click http://www.wileyauthors.com/help?utm_source=new%20user%20invitation&utm_medium=email here to view our Help section.
     1237
     1238Sincerely,
     1239Wiley Author Services
     1240
     1241P.S. - Some journals accept artwork submissions for Cover Images. This is an optional service you can use to help increase article exposure and showcase your research. Pricing and placement options vary by journal. For more information, including artwork guidelines, pricing, and submission details, please visit the https://authorservices.wiley.com/author-resources/Journal-Authors/Promotion/journal-cover-image.html?utm_source=as&utm_medium=email&utm_term=invitation_msg&utm_content=covers&utm_campaign=2019feb?campaign=email_invitation-new" target=_blank">Journal Cover Image page. If you want help creating an image, Wiley Editing Services offers a professional https://wileyeditingservices.com/en/article-promotion/cover-image-design.html?utm_source=as&utm_medium=email&utm_term=ie&utm_content=cid&utm_campaign=prodops" target=_blank">Cover Image Design service that creates eye-catching images, ready to be showcased on the journal cover.
     1242
     1243
     1244
     1245Date: Thu, 22 Oct 2020 20:21:49 +0000
     1246From: <cs-author@wiley.com>
     1247To: <pabuhr@uwaterloo.ca>
     1248Subject: You have actions to complete in Author Services
     1249
     1250Dear Peter Buhr,
     1251
     1252Article ID: SPE2925
     1253Article DOI: 10.1002/spe.2925
     1254Internal Article ID: 16922213
     1255Article: Advanced Control-flow and Concurrency in C A
     1256Journal: Software: Practice and Experience
     1257
     1258For the above article, you have the following open tasks:
     1259
     1260Sign your license agreement in order to publish your article. Simply click the Sign License button on your https://authorservices.wiley.com?campaign=email_license-notice1">Wiley Author Services Dashboard.
     1261
     1262Need any help? Please visit our https://authorsupport.wiley.com/s/">Author Support Center.
     1263
     1264Sincerely,
     1265Wiley Author Services
     1266
     1267
     1268
     1269Date: Thu, 22 Oct 2020 23:13:07 +0000
     1270From: <cs-author@wiley.com>
     1271To: <pabuhr@uwaterloo.ca>
     1272Subject: License was successfully submitted! Thank you!
     1273
     1274Dear Peter Buhr,                                                                 
     1275
     1276Article ID: SPE2925
     1277Article DOI: 10.1002/spe.2925
     1278Internal Article ID: 16922213
     1279Article: Advanced Control-flow and Concurrency in C A 
     1280Journal: Software: Practice and Experience                                                                     
     1281                                                                         
     1282You've successfully completed license signing for your article - thank you! You can view your signed agreement at any time by visiting your https://authorservices.wiley.com?campaign=email_license-confirm">Wiley Author Services Dashboard.                                                                     
     1283
     1284Sincerely,                                                                                 
     1285
     1286Wiley Author Services
     1287
     1288
     1289
     1290From: "Pacaanas, Joel -" <jpacaanas@wiley.com>
     1291To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
     1292CC: Thierry Delisle <tdelisle@uwaterloo.ca>
     1293Subject: RE: Action: Proof of SPE_EV_SPE2925 for Software: Practice And Experience ready for review
     1294Date: Thu, 5 Nov 2020 02:03:27 +0000
     1295
     1296Dear Dr Buhr,
     1297
     1298Thank you for letting me know. We will wait for your corrections then.
     1299
     1300Best regards,
     1301Joel
     1302
     1303Joel Q. Pacaanas
     1304Production Editor
     1305On behalf of Wiley
     1306Manila
     1307We partner with global experts to further innovative research.
     1308
     1309E-mail: jpacaanas@wiley.com
     1310Tel: +632 88558618
     1311Fax: +632 5325 0768
     1312
     1313-----Original Message-----
     1314From: Peter A. Buhr [mailto:pabuhr@uwaterloo.ca]
     1315Sent: Thursday, November 5, 2020 5:57 AM
     1316To: SPE Proofs <speproofs@wiley.com>
     1317Cc: Thierry Delisle <tdelisle@uwaterloo.ca>
     1318Subject: Re: Action: Proof of SPE_EV_SPE2925 for Software: Practice And Experience ready for review
     1319
     1320       This is an external email.
     1321
     1322    We appreciate that the COVID-19 pandemic may create conditions for you that
     1323    make it difficult for you to review your proof within standard time
     1324    frames. If you have any problems keeping to this schedule, please reach out
     1325    to me at (SPEproofs@wiley.com) to discuss alternatives.
     1326
     1327Hi,
     1328
     1329We are in the middle of reading the proofs but it will take a little more
     1330time. I can send the proofs back by Monday Nov 9, but probably earlier.
     1331
     1332
     1333
     1334From: "Pacaanas, Joel -" <jpacaanas@wiley.com>
     1335To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
     1336CC: "tdelisle@uwaterloo.ca" <tdelisle@uwaterloo.ca>
     1337Subject: RE: Action: Proof of SPE_EV_SPE2925 for Software: Practice And Experience ready for review
     1338Date: Fri, 20 Nov 2020 05:27:18 +0000
     1339
     1340Dear Peter,
     1341
     1342We have now reset the proof back to original stage. Please refer to the below editable link.
     1343
     1344https://wiley.eproofing.in/Proof.aspx?token=ab7739d5678447fbbe5036f3bcba2445081500061
     1345
     1346Since the proof was reset, your added corrections before has also been removed. Please add them back.
     1347
     1348Please return your corrections at your earliest convenience.
     1349
     1350Best regards,
     1351Joel
     1352
     1353Joel Q. Pacaanas
     1354Production Editor
     1355On behalf of Wiley
     1356Manila
     1357We partner with global experts to further innovative research.
     1358
     1359E-mail: jpacaanas@wiley.com
     1360Tel: +632 88558618
     1361Fax: +632 5325 0768
     1362
     1363
     1364 
     1365From: "Wiley Online Proofing" <notifications@eproofing.in>
     1366To: pabuhr@uwaterloo.ca
     1367Cc: SPEproofs@wiley.com
     1368Reply-To: eproofing@wiley.com
     1369Date: 26 Nov 2020 18:57:27 +0000
     1370Subject: Corrections successfully submitted for SPE_EV_SPE2925, Advanced control-flow in Cforall.
     1371
     1372Corrections successfully submitted
     1373
     1374Dear Dr. Peter Buhr,
     1375
     1376Thank you for reviewing the proof of the Software: Practice And Experience article Advanced control-flow in Cforall.
     1377
     1378View Article https://wiley.eproofing.in/Proof.aspx?token=ab7739d5678447fbbe5036f3bcba2445081500061
     1379
     1380This is a read-only version of your article with the corrections you have marked up.
     1381
     1382If you encounter any problems or have questions please contact me, Joel Pacaanas at (SPEproofs@wiley.com). For the quickest response include the journal name and your article ID (found in the subject line) in all correspondence.
     1383
     1384Best regards,
     1385Joel Pacaanas
  • doc/proposals/vtable.md

    r3c64c668 r58fe85a  
    237237default is provided or not, the second syntax can be used to pick a
    238238parameter on instantiation.
     239
     240### Extension: Object Access
     241This requires that the resolution scope (see below) is at the type level or
     242has explicate points with names. These are the tables and table names used
     243here.
     244
     245The system already knows where to find the virtual table and the object. If
     246the tables have particular identities, or on the user side names, then it is
     247meaningful to check if a binding virtual table is the same* as another. The
     248main use of this is virtual table declarations also give the type they bind
     249and if a binding table matches a known table then the underlyind object in the
     250trait object must be of that type.
     251
     252* By identity, by value would work and in some senses be more flexiable. But
     253  it would be slower and refering to further away functions would be harder.
     254
     255This gives one of the main new features of the hierarchical use of virtual
     256tables (see below); the ability to recover the underlying object. Or a pointer
     257of the approprate type it which both reflects the implementation and gives a
     258convenent way to encode the boolean/conditional aspect of the operation which
     259is that a different virtual table might be in use.
     260
     261There are two general ways to reperent this; a cast or a field access. The
     262cast is traditional and would definitely fit if a single pointer repersents
     263a trait object with the virtual table as part of the object. However for a
     264double pointer field access might be more approprate. By this system though
     265it is not the type that is used as the identifier but the virtual table. If
     266there is one table per type than it becomes equivilant again. Otherwise the
     267table has to be used as the identifier and the type is just a result of that
     268which seems important for syntax.
    239269
    240270Hierarchy
     
    482512possibly like the one used to create the assertion.
    483513
     514### Extension: Associated Types Use
     515If the `associated_types.md` proposal is accepted the following trait could
     516be added:
     517
     518    trait is_virtual(dtype T) {
     519        dtype table;
     520        // An example assertion:
     521        const table & get_virtual_table(T &);
     522    }
     523
     524There may be more assertions but there has to be at least one way to find
     525the (possibly default) virtual table. It is required to construct instances
     526of the type.
     527
     528Without the assotiated type it would look like this:
     529
     530    trait is_virtual(dtype T, dtype table) {
     531        const table & get_virtual_table(T &);
     532    }
     533
     534Which is just a little bit longer to use but becomes more problematic if the
     535user has to explicately provide the table's name as it doesn't really have its
     536own type name. If it does it is probably mangled.
     537
    484538### Virtual Tables as Types
    485539Here we consider encoding plus the implementation of functions on it to be a
     
    560614be used in only some of the declarations.
    561615
    562     trait combiner fee = (summation_instance, sum);
     616    trait combiner fee = {summation_instance, sum};
    563617    trait combiner foe = summation_instance;
    564618
  • doc/refrat/refrat.tex

    r3c64c668 r58fe85a  
    1111%% Created On       : Wed Apr  6 14:52:25 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Wed Jan 31 17:30:23 2018
    14 %% Update Count     : 108
     13%% Last Modified On : Mon Oct  5 09:02:53 2020
     14%% Update Count     : 110
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    3030\usepackage{upquote}                                                                    % switch curled `'" to straight
    3131\usepackage{calc}
    32 \usepackage{xspace}
    3332\usepackage{varioref}                                                                   % extended references
    34 \usepackage{listings}                                                                   % format program code
    3533\usepackage[flushmargin]{footmisc}                                              % support label/reference in footnote
    3634\usepackage{latexsym}                                   % \Box glyph
    3735\usepackage{mathptmx}                                   % better math font with "times"
    3836\usepackage[usenames]{color}
    39 \input{common}                                          % common CFA document macros
    40 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}
    41 \usepackage{breakurl}
    42 \renewcommand{\UrlFont}{\small\sf}
    43 
    44 \usepackage[pagewise]{lineno}
    45 \renewcommand{\linenumberfont}{\scriptsize\sffamily}
    46 \usepackage[firstpage]{draftwatermark}
    47 \SetWatermarkLightness{0.9}
    48 
    49 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore
    50 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR
    51 % AFTER HYPERREF.
    52 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}
    53 
    54 \setlength{\topmargin}{-0.45in}                                                 % move running title into header
    55 \setlength{\headsep}{0.25in}
    56 
    57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    58 
    59 \CFAStyle                                                                                               % use default CFA format-style
    60 \lstnewenvironment{C++}[1][]                            % use C++ style
    61 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®}#1}}
    62 {}
    63 
     37\newcommand{\CFALatin}{}
    6438% inline code Ā©...Ā© (copyright symbol) emacs: C-q M-)
    6539% red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-.
     
    6943% keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^
    7044% math escape $...$ (dollar symbol)
     45\input{common}                                          % common CFA document macros
     46\usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}
     47\usepackage{breakurl}
     48\renewcommand{\UrlFont}{\small\sf}
     49
     50\usepackage[pagewise]{lineno}
     51\renewcommand{\linenumberfont}{\scriptsize\sffamily}
     52\usepackage[firstpage]{draftwatermark}
     53\SetWatermarkLightness{0.9}
     54
     55% Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore
     56% removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR
     57% AFTER HYPERREF.
     58\renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}
     59
     60\setlength{\topmargin}{-0.45in}                                                 % move running title into header
     61\setlength{\headsep}{0.25in}
    7162
    7263%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    7364
     65\CFAStyle                                                                                               % use default CFA format-style
     66\lstnewenvironment{C++}[1][]                            % use C++ style
     67{\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}}
     68{}
     69
     70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     71
    7472% Names used in the document.
    75 \newcommand{\Version}{\input{../../version}}
     73\newcommand{\Version}{\input{build/version}}
    7674\newcommand{\Textbf}[2][red]{{\color{#1}{\textbf{#2}}}}
    7775\newcommand{\Emph}[2][red]{{\color{#1}\textbf{\emph{#2}}}}
  • doc/theses/thierry_delisle_PhD/.gitignore

    r3c64c668 r58fe85a  
    88
    99comp_II/build/
     10comp_II/img/*.fig.bak
    1011comp_II/comp_II.pdf
    1112comp_II/comp_II.ps
     13comp_II/presentation.pdf
     14
     15thesis/build/
     16thesis/fig/*.fig.bak
     17thesis/thesis.pdf
     18thesis/thesis.ps
    1219
    1320!Makefile
  • doc/theses/thierry_delisle_PhD/comp_II/Makefile

    r3c64c668 r58fe85a  
    22
    33Build = build
    4 Figures = figures
     4Figures = img
    55Macros = ../../../LaTeXmacros
    66TeXLIB = .:${Macros}:${Build}:../../../bibliography:
     
    1212
    1313## Define the text source files.
    14 
    15 SOURCES = ${addsuffix .tex, \
    16 comp_II \
    17 }
    18 
    1914FIGURES = ${addsuffix .tex, \
     15        emptybit \
     16        emptytree \
     17        emptytls \
     18        resize \
    2019}
    2120
    2221PICTURES = ${addsuffix .pstex, \
     22        base \
     23        empty \
     24        system \
    2325}
    2426
     
    3032
    3133## Define the documents that need to be made.
     34all: comp_II.pdf presentation.pdf
     35comp_II.pdf: ${FIGURES} ${PICTURES}
     36presentation.pdf: presentationstyle.sty base.dark.pstex empty.dark.pstex system.dark.pstex
    3237
    33 DOCUMENT = comp_II.pdf
     38DOCUMENT = comp_II.pdf presentation.pdf
    3439BASE = ${basename ${DOCUMENT}}
    3540
     
    4550# File Dependencies #
    4651
    47 ${DOCUMENT} : ${BASE}.ps
     52%.pdf : build/%.ps | ${Build}
    4853        ps2pdf $<
    4954
    50 ${BASE}.ps : ${BASE}.dvi
    51         dvips ${Build}/$< -o $@
     55build/%.ps : build/%.dvi | ${Build}
     56        dvips $< -o $@
    5257
    53 ${BASE}.dvi : Makefile ${GRAPHS} ${PROGRAMS} ${PICTURES} ${FIGURES} ${SOURCES} \
    54                 ${Macros}/common.tex ${Macros}/indexstyle ../../../bibliography/pl.bib \
    55                 local.bib glossary.tex | ${Build}
     58build/%.dvi : %.tex Makefile | ${Build}
    5659        # Must have *.aux file containing citations for bibtex
    57         if [ ! -r ${basename $@}.aux ] ; then ${LaTeX} ${basename $@}.tex ; fi
    58         -${BibTeX} ${Build}/${basename $@}
     60        if [ ! -r ${basename $@}.aux ] ; then ${LaTeX} $< ; fi
     61        -${BibTeX} ${basename $@}
    5962        # Some citations reference others so run again to resolve these citations
    60         ${LaTeX} ${basename $@}.tex
    61         -${BibTeX} ${Build}/${basename $@}
     63        ${LaTeX} $<
     64        -${BibTeX} ${basename $@}
    6265        # Make index from *.aux entries and input index at end of document
    63         makeglossaries -q -s ${Build}/${basename $@}.ist ${Build}/${basename $@}
     66        -makeglossaries -q -s ${basename $@}.ist ${basename $@}
    6467        # Run again to finish citations
    65         ${LaTeX} ${basename $@}.tex
     68        ${LaTeX} $<
    6669
    6770## Define the default recipes.
     
    7073        mkdir -p ${Build}
    7174
    72 %.tex : %.fig ${Build}
     75%.tex : img/%.fig | ${Build}
    7376        fig2dev -L eepic $< > ${Build}/$@
    7477
    75 %.ps : %.fig | ${Build}
     78%.ps : img/%.fig | ${Build}
    7679        fig2dev -L ps $< > ${Build}/$@
    7780
    78 %.pstex : %.fig | ${Build}
     81%.pstex : img/%.fig | ${Build}
    7982        fig2dev -L pstex $< > ${Build}/$@
     83        fig2dev -L pstex_t -p ${Build}/$@ $< > ${Build}/$@_t
     84
     85## pstex with inverted colors
     86%.dark.pstex : img/%.fig Makefile | ${Build}
     87        fig2dev -L pstex $< > ${Build}/$@
     88        sed -i 's/\/col-1 {0 setgray} bind def/\/col-1 {1 setgray} bind def/g' ${Build}/$@
     89        sed -i 's/\/col0 {0.000 0.000 0.000 srgb} bind def/\/col0 {1.000 1.000 1.000 srgb} bind def/g' ${Build}/$@
     90        sed -i 's/\/col7 {1.000 1.000 1.000 srgb} bind def/\/col7 {0.000 0.000 0.000 srgb} bind def/g' ${Build}/$@
    8091        fig2dev -L pstex_t -p ${Build}/$@ $< > ${Build}/$@_t
    8192
  • doc/theses/thierry_delisle_PhD/comp_II/comp_II.tex

    r3c64c668 r58fe85a  
    1 \documentclass[11pt,fullpage]{article}
     1\documentclass[11pt]{article}
     2\usepackage{fullpage}
    23\usepackage[T1]{fontenc}
    34\usepackage[utf8]{inputenc}
    4 \usepackage{listings}           % for code listings
    55\usepackage{xspace}
    66\usepackage{xcolor}
    77\usepackage{graphicx}
    8 \usepackage[hidelinks]{hyperref}
     8\usepackage{epic,eepic}
     9\usepackage{listings}                   % for code listings
    910\usepackage{glossaries}
    1011\usepackage{textcomp}
    11 \usepackage{geometry}
    12 
    1312% cfa macros used in the document
    1413\input{common}
     14
     15\setlist{topsep=6pt,parsep=0pt}         % global reduce spacing between points
     16\newcommand{\uC}{$\mu$\CC}
     17\usepackage[hidelinks]{hyperref}
     18\setlength{\abovecaptionskip}{5pt plus 3pt minus 2pt}
     19\lstMakeShortInline$%                   % single-character for \lstinline
     20%\usepackage[margin=1in]{geometry}
     21%\usepackage{float}
     22
    1523\input{glossary}
    1624
     
    2432
    2533\author{
    26         \huge Thierry Delisle \\
    27         \Large \vspace*{0.1in} \texttt{tdelisle@uwaterloo.ca} \\
     34        \huge Thierry Delisle \vspace*{5pt} \\
     35        \Large \texttt{tdelisle@uwaterloo.ca} \vspace*{5pt} \\
    2836        \Large Cheriton School of Computer Science \\
    2937        \Large University of Waterloo
     
    3644\begin{document}
    3745\maketitle
     46\thispagestyle{empty}
    3847\cleardoublepage
    3948
    4049\newcommand{\cit}{\textsuperscript{[Citation Needed]}\xspace}
    41 \newcommand{\TODO}{~\newline{\large\bf\color{red} TODO :}\xspace}
     50\newcommand{\TODO}{{\large\bf\color{red} TODO: }\xspace}
    4251
    4352% ===============================================================================
     
    5160\section{Introduction}
    5261\subsection{\CFA and the \CFA concurrency package}
    53 \CFA\cit is a modern, polymorphic, non-object-oriented, backwards-compatible extension of the C programming language. It aims to add high productivity features while maintaning the predictible performance of C. As such concurrency in \CFA\cit aims to offer simple and safe high-level tools while still allowing performant code. Concurrent code is written in the syncrhonous programming paradigm but uses \glspl{uthrd} in order to achieve the simplicity and maintainability of synchronous programming without sacrificing the efficiency of asynchronous programing. As such the \CFA scheduler is a user-level scheduler that maps \glspl{uthrd} onto \glspl{kthrd}.
    54 
    55 The goal of this research is to produce a scheduler that is simple to use and offers acceptable performance in all cases. Here simplicity does not refer to the API but to how much scheduling concerns programmers need to take into account when using the \CFA concurrency package. Therefore, the main goal of this proposal is as follows :
     62\CFA~\cite{Moss18} is a modern, polymorphic, non-object-oriented, concurrent, backwards-compatible extension of the C programming language.
     63It aims to add high-productivity features while maintaining the predictable performance of C.
     64As such, concurrency in \CFA~\cite{Delisle19} aims to offer simple and safe high-level tools while still allowing performant code.
     65\CFA concurrent code is written in the synchronous programming paradigm but uses \glspl{uthrd} to achieve the simplicity and maintainability of synchronous programming without sacrificing the efficiency of asynchronous programming.
     66As such, the \CFA \newterm{scheduler} is a preemptive user-level scheduler that maps \glspl{uthrd} onto \glspl{kthrd}.
     67
     68\subsection{Scheduling}
     69\newterm{Scheduling} occurs when execution switches from one thread to another, where the second thread is implicitly chosen by the scheduler.
     70This scheduling is an indirect handoff, as opposed to generators and coroutines that explicitly switch to the next generator and coroutine respectively.
     71The cost of switching between two threads for an indirect handoff has two components:
     72\begin{enumerate}
     73\item
     74the cost of actually context-switching, \ie changing the relevant registers to move execution from one thread to the other,
     75\item
     76and the cost of scheduling, \ie deciding which thread to run next among all the threads ready to run.
     77\end{enumerate}
     78The first cost is generally constant\footnote{Affecting the constant context-switch cost is whether it is done in one step, where the first thread schedules the second, or in two steps, where the first thread context switches to a third scheduler thread.}, while the scheduling cost can vary based on the system state.
     79Adding multiple \glspl{kthrd} does not fundamentally change the scheduler semantics or requirements, it simply adds new correctness requirements, \ie \newterm{linearizability}\footnote{Meaning however fast the CPU threads run, there is an equivalent sequential order that gives the same result.}, and a new dimension to performance: scalability, where scheduling cost also depends on contention.
     80The more threads switch, the more the administration cost of scheduling becomes noticeable.
     81It is therefore important to build a scheduler with the lowest possible cost and latency.
     82Another important consideration is \newterm{fairness}.
     83In principle, scheduling should give the illusion of perfect fairness, where all threads ready to run are running \emph{simultaneously}.
     84In practice, there can be advantages to unfair scheduling, similar to the express cash register at a grocery store.
     85While the illusion of simultaneity is easier to reason about, it can break down if the scheduler allows too much unfairness.
     86Therefore, the scheduler should offer as much fairness as needed to guarantee eventual progress, but use unfairness to help performance.
     87
     88\subsection{Research Goal}
     89The goal of this research is to produce a scheduler that is simple for programmers to understand and offers good general performance.
     90Here understandability does not refer to the API but to how much scheduling concerns programmers need to take into account when writing a \CFA concurrent package.
     91Therefore, the main consequence of this goal is :
    5692\begin{quote}
    57 The \CFA scheduler should be \emph{viable} for any workload.
     93The \CFA scheduler should be \emph{viable} for \emph{any} workload.
    5894\end{quote}
    5995
    60 This objective includes producing a scheduling strategy with minimal fairness guarantees, creating an abstraction layer over the operating system to handle kernel-threads spinning unnecessarily and hide blocking I/O operations and, writing sufficient library tools to allow developpers to properly use the scheduler.
    61 
    62 % ===============================================================================
    63 % ===============================================================================
    64 
    65 \section{Scheduling for \CFA}
    66 While the \CFA concurrency package doesn't have any particular scheduling needs beyond those of any concurrency package which uses \glspl{uthrd}, it is important that the default \CFA Scheduler be viable in general. Indeed, since the \CFA Scheduler does not target any specific workloads, it is unrealistic to demand that it use the best scheduling strategy in all cases. However, it should offer a viable ``out of the box'' solution for most scheduling problems so that programmers can quickly write performant concurrent without needed to think about which scheduling strategy is more appropriate for their workload. Indeed, only programmers with exceptionnaly high performance requirements should need to write their own scheduler. More specifically, two broad types of schedulering strategies should be avoided in order to avoid penalizing certain types of workloads : feedback-based and priority schedulers.
     96For a general-purpose scheduler, it is impossible to produce an optimal algorithm as that requires knowledge of the future behaviour of threads.
     97As such, scheduling performance is generally either defined by a best-case scenario, \ie a workload to which the scheduler is tailored, or a worst-case scenario, \ie the scheduler behaves no worse than \emph{X}.
     98For this proposal, the performance is evaluated using the second approach to allow \CFA programmers to rely on scheduling performance.
     99Because there is no optimal scheduler, ultimately \CFA may allow programmers to write their own scheduler; but that is not the subject of this proposal, which considers only the default scheduler.
     100As such, it is important that only programmers with exceptionally high performance requirements should need to write their own scheduler and replace the scheduler in this proposal.
     101
     102To achieve the \CFA scheduling goal includes:
     103\begin{enumerate}
     104        \item producing a scheduling strategy with sufficient fairness guarantees,
     105        \item creating an abstraction layer over the operating system to handle kernel-threads spinning unnecessarily,
     106        \item scheduling blocking I/O operations,
     107        \item and writing sufficient library tools to allow developers to indirectly use the scheduler, either through tuning knobs in the default scheduler or replacing the default scheduler.
     108\end{enumerate}
     109
     110% ===============================================================================
     111% ===============================================================================
     112
     113\section{\CFA Scheduling}
     114To schedule user-level threads across all workloads, the scheduler has a number of requirements:
     115
     116\paragraph{Correctness} As with any other concurrent data structure or algorithm, the correctness requirement is paramount.
     117The scheduler cannot allow threads to be dropped from the ready queue, \ie scheduled but never run, or be executed multiple times when only being scheduled once.
     118Since \CFA concurrency has no spurious wake up, this definition of correctness also means the scheduler should have no spurious wake up.
     119The \CFA scheduler must be correct.
     120
     121\paragraph{Performance} The performance of a scheduler can generally be measured in terms of scheduling cost, scalability and latency.
     122\newterm{Scheduling cost} is the cost to switch from one thread to another, as mentioned above.
     123For compute-bound concurrent applications with little context switching, the scheduling cost is negligible.
     124For applications with high context-switch rates, scheduling cost can begin to dominating the cost.
     125\newterm{Scalability} is the cost of adding multiple kernel threads.
     126It can increase the time for scheduling because of contention from the multiple threads accessing shared resources, \eg a single ready queue.
     127Finally, \newterm{tail latency} is service delay and relates to thread fairness.
     128Specifically, latency measures how long a thread waits to run once scheduled and is evaluated by the worst case.
     129The \CFA scheduler should offer good performance for all three metrics.
     130
     131\paragraph{Fairness} Like performance, this requirement has several aspects : eventual progress, predictability and performance reliability.
     132\newterm{Eventual progress} guarantees every scheduled thread is eventually run, \ie prevent starvation.
     133As a hard requirement, the \CFA scheduler must guarantee eventual progress, otherwise the above-mentioned illusion of simultaneous execution is broken and the scheduler becomes much more complex to reason about.
     134\newterm{Predictability} and \newterm{reliability} mean similar workloads achieve similar performance so programmer execution intuition is respected.
     135For example, a thread that yields aggressively should not run more often than other threads.
     136While this is intuitive, it does not hold true for many work-stealing or feedback based schedulers.
     137The \CFA scheduler must guarantee eventual progress, should be predictable, and offer reliable performance.
     138
     139\paragraph{Efficiency} Finally, efficient usage of CPU resources is also an important requirement and is discussed in depth towards the end of the proposal.
     140\newterm{Efficiency} means avoiding using CPU cycles when there are no threads to run (to conserve energy), and conversely, using as many available CPU cycles when the workload can benefit from it.
     141Balancing these two states is where the complexity lies.
     142The \CFA scheduler should be efficient with respect to the underlying (shared) computer.
     143
     144\bigskip To achieve these requirements, I can reject two broad types of scheduling strategies : feedback-based and priority schedulers.
    67145
    68146\subsection{Feedback-Based Schedulers}
    69 Many operating systems use schedulers based on feadback loops in some form, they measure how much CPU a particular thread has used\footnote{Different metrics can be used to here but it is not relevant to the discussion.} and schedule threads based on this metric. These strategies are sensible for operating systems but rely on two assumptions on the workload :
    70 
    71 \begin{enumerate}
    72         \item Threads live long enough to be scheduled many times.
    73         \item Cooperation among all threads is not simply infeasible, it is a security risk.
    74 \end{enumerate}
    75 
    76 While these two assumptions generally hold for operating systems, they may not for \CFA programs. In fact, \CFA uses \glspl{uthrd} which have the explicit goal of reducing the cost of threading primitives to allow many smaller threads. This can naturally lead to have threads with much shorter lifetime and only being scheduled a few times. Scheduling strategies based on feadback loops cannot be effective in these cases because they will not have the opportunity to measure the metrics that underlay the algorithm. Note that the problem of feadback loop convergence (reacting too slowly to scheduling events) is not specific to short lived threads but can also occur with threads that show drastic changes in scheduling event, e.g., threads running for long periods of time and then suddenly blocking and unblocking quickly and repeatedly.
    77 
    78 In the context of operating systems, these concerns can be overshadowed by a more pressing concern : security. When multiple users are involved, it is possible that some users are malevolent and try to exploit the scheduling strategy in order to achieve some nefarious objective. Security concerns mean that more precise and robust fairness metrics must be used. In the case of the \CFA scheduler, every thread runs in the same user-space and are controlled from the same user. It is then possible to safely ignore the possibility that threads are malevolent and assume that all threads will ignore or cooperate with each other. This allows for a much simpler fairness metric and in this proposal ``fairness'' will be considered as equal opportunities to run once scheduled.
    79 
    80 Since feadback is not necessarily feasible within the lifetime of all threads and a simple fairness metric can be used, the scheduling strategy proposed for the \CFA runtime does not user per-threads feedback. Feedback loops in general are not rejected for secondary concerns like idle sleep, but no feedback loop is used to decide which thread to run next.
     147Many operating systems use schedulers based on feedback in some form, \eg measuring how much CPU a particular thread has used\footnote{Different metrics can be measured but it is not relevant to the discussion.} and schedule threads based on this metric.
     148These strategies are sensible for operating systems but rely on two assumptions for the workload:
     149
     150\begin{enumerate}
     151        \item Threads live long enough for useful feedback information to be gathered.
     152        \item Threads belong to multiple users so fairness across users is important.
     153\end{enumerate}
     154
     155While these two assumptions generally hold for operating systems, they may not for user-level threading.
     156Since \CFA has the explicit goal of allowing many smaller threads, this can naturally lead to threads with much shorter lifetimes that are only scheduled a few times.
     157Scheduling strategies based on feedback cannot be effective in these cases because there is no opportunity to measure the metrics that underlie the algorithm.
     158Note, the problem of \newterm{feedback convergence} (reacting too slowly to scheduling events) is not specific to short-lived threads but can also occur with threads that show drastic changes in scheduling, \eg threads running for long periods of time and then suddenly blocking and unblocking quickly and repeatedly.
     159
     160In the context of operating systems, these concerns can be overshadowed by a more pressing concern : security.
     161When multiple users are involved, it is possible some users are malevolent and try to exploit the scheduling strategy to achieve some nefarious objective.
     162Security concerns mean more precise and robust fairness metrics must be used to guarantee fairness across processes created by users as well as threads created within a process.
     163In the case of the \CFA scheduler, every thread runs in the same user space and is controlled by the same user.
     164Fairness across users is therefore a given and it is then possible to safely ignore the possibility that threads are malevolent.
     165This approach allows for a much simpler fairness metric, and in this proposal, \emph{fairness} is defined as:
     166\begin{quote}
     167When multiple threads are cycling through the system, the total ordering of threads being scheduled, \ie pushed onto the ready queue, should not differ much from the total ordering of threads being executed, \ie popped from the ready queue.
     168\end{quote}
     169
     170Since feedback is not necessarily feasible within the lifetime of all threads and a simple fairness metric can be used, the scheduling strategy proposed for the \CFA runtime does not use per-threads feedback.
     171Feedback in general is not rejected for secondary concerns like idle sleep for kernel threads, but no feedback is used to decide which thread to run next.
    81172
    82173\subsection{Priority Schedulers}
    83 Another broad category of schedulers are priority schedulers. In these scheduling strategies threads have priorities and the runtime schedules the threads with the highest priority before scheduling other threads. Threads with equal priority are scheduled using a secondary strategy, often something simple like round-robin or FIFO. These priority mean that, as long as there is a thread with a higher priority that desires to run, a thread with a lower priority will not run. This possible starving of threads can dramatically increase programming complexity since starving threads and priority inversion (prioritising a lower priority thread) can both lead to serious problems, leaving programmers between a rock and a hard place.
    84 
    85 An important observation to make is that threads do not need to have explicit priorities for problems to be possible. Indeed, any system with multiple ready-queues and attempts to exhaust one queue before accessing the other queues, could encounter starvation problems. A popular scheduling strategy that suffers from implicit priorities is work-stealing. Work-stealing is generally presented as follows :
    86 
    87 \begin{itemize}
    88         \item Each processor has a list of threads.
    89 \end{itemize}
    90 \begin{enumerate}
    91         \item Run threads from ``this'' processor's list.
    92         \item If ``this'' processor's list is empty, run threads from some other processor's list.
    93 \end{enumerate}
    94 
    95 In a loaded system\footnote{A loaded system is a system where threads are being run at the same rate they are scheduled}, if a thread does not yield or block for an extended period of time, threads on the same processor list will starve if no other processors can exhaust their list.
    96 
    97 Since priorities can be complex to handle for programmers, the scheduling strategy proposed for the \CFA runtime does not use a strategy with either implicit or explicit thread priorities.
    98 
    99 \subsection{Schedulers without feadback or priorities}
    100 I claim that the ideal default scheduler for the \CFA runtime is a scheduler that offers good scalability and a simple fairness guarantee that is easy for programmers to reason about. The simplest fairness guarantee is to guarantee FIFO ordering, i.e., threads scheduled first will run first. However, enforcing FIFO ordering generally conflicts with scalability across multiple processors because of the additionnal synchronization. Thankfully, strict FIFO is not needed for scheduling. Since concurrency is inherently non-deterministic, fairness concerns in scheduling are only a problem if a thread repeatedly runs before another thread can run\footnote{This is because the non-determinism means that programmers must already handle ordering problems in order to produce correct code and already must rely on weak guarantees, for example that a specific thread will \emph{eventually} run.}. This need for unfairness to persist before problems occur means that the FIFO fairness guarantee can be significantly relaxed without causing problems. For this proposal, the target guarantee is that the \CFA scheduler guarantees \emph{probable} FIFO ordering, which is defined as follows :
    101 \begin{itemize}
    102         \item Given two threads $X$ and $Y$, the odds that thread $X$ runs $N$ times \emph{after} thread $Y$ is scheduled but \emph{before} it is run, decreases exponentially with regards to $N$.
    103 \end{itemize}
    104 
    105 While this is not a strong guarantee, the probability that problems persist for long period of times decreases exponentially, making persisting problems virtually impossible.
    106 
    107 \subsection{Real-Time}
    108 While the objective of this proposed scheduler is similar to the objective of real-time scheduling, this proposal is not a proposal for real-time scheduler and as such makes no attempt to offer either soft or hard guarantees on scheduling delays.
    109 
    110 % ===============================================================================
    111 % ===============================================================================
    112 \section{Proposal}
    113 
    114 \subsection{Ready-Queue}
    115 Using trevor's paper\cit as basis, it is simple to build a relaxed FIFO list that is fast and scalable for loaded or overloaded systems. The described queue uses an array of underlying strictly FIFO queue. Pushing new data is done by selecting one of these underlying queues at random, recording a timestamp for the push and pushing to the selected queue. Popping is done by selecting two queues at random and popping from the queue for which the head has the oldest timestamp. In loaded or overloaded systems, it is higly likely that the queues is far from empty, e.i., several tasks are on each of the underlying queues. This means that selecting a queue at random to pop from is higly likely to yield a queue that is not empty.
    116 
    117 When the ready queue is "more empty", i.e., several of the inner queues are empty, selecting a random queue for popping is less likely to yield a valid selection and more attempts need to be made, resulting in a performance degradation. In cases, with few elements on the ready queue and few processors running, performance can be improved by adding information to help processors find which inner queues are used. Preliminary performance tests indicate that with few processors, a bitmask can be used to identify which inner queues are currently in use. This is especially effective in the single-thread case, where the bitmask will always be up-to-date. Furthermore, modern x86 CPUs have a BMI2 extension which allow using the bitmask with very little overhead over directly accessing the readyqueue offerring decent performance even in cases with many empty inner queues. This technique does not solve the problem completely, it randomly attempts to find a block of 64 queues where at least one is used, instead of attempting to find a used queue. For systems with a large number of cores this does not completely solve the problem, but it is a fixed improvement. The size of the blocks are limited by the maximum size atomic instruction can operate on, therefore atomic instructions on large words would increase the 64 queues per block limit.
    118 
    119 \TODO double check the next sentence
    120 Preliminary result indicate that the bitmask approach with the BMI2 extension can lead to multi-threaded performance that is contention agnostic in the worst case.
    121 This result suggests that the contention penalty and the increase performance for additionnal thread cancel each other exactly. This may indicate that a relatively small reduction in contention may tip the performance into positive scalling even for the worst case. It can be noted that in cases of high-contention, the use of the bitmask to find queues that are not empty is much less reliable. Indeed, if contention on the bitmask is high, it means it probably changes significantly between the moment it is read and the actual operation on the queues it represents. Furthermore, the objective of the bitmask is to avoid probing queues that are empty. Therefore, in cases where the bitmask is highly contented, it may be preferrable to probe queues randomly, either until contention decreases or until a prior prefetch of the bitmask completes. Ideally, the scheduler would be able to observe that the bitmask is highly contented and adjust its behaviour appropriately. However, I am not aware of any mechanism to query whether a cacheline is in cache or to run other instructions until a cacheline is fetch without blocking on the cacheline. As such, an alternative that may have a similar impact would be for each thread to have their own bitmask, which would be updated both after each scheduler action and after a certain number of failed probing. If the bitmask has little contention, the local bitmask will be mostly up-to-date and several threads won't need to contend as much on the global bitmask. If the bitmask has significant contention, then fetching it becomes more expensive and threads may as well probe randomly. This solution claims that probing randomly or against an out-of-date bitmask is equivalent.
    122 
    123 In cases where this is insufficient, another approach is to use a hiearchical data structure. Creating a tree of nodes to reduce contention has been shown to work in similar cases\cit(SNZI: Scalable NonZero Indicators)\footnote{This particular paper seems to be patented in the US. How does that affect \CFA? Can I use it in my work?}. However, this approach may lead to poorer single-threaded performance due to the inherent pointer chasing, as such, it was not considered as the first approach but as a fallback in case the bitmask approach does not satisfy the performance goals.
    124 
    125 Part of this performance relies on contention being low when there are few threads on the readyqueue. However, this can be assumed reliably if the system handles putting idle processors to sleep, which is addressed in section \ref{sleep}.
     174Another broad category of schedulers are priority schedulers.
     175In these scheduling strategies, threads have priorities and the runtime schedules the threads with the highest priority before scheduling other threads.
     176Threads with equal priority are scheduled using a secondary strategy, often something simple like round robin or FIFO.
     177A consequence of priority is that, as long as there is a thread with a higher priority that desires to run, a thread with a lower priority does not run.
     178The potential for thread starvation dramatically increases programming complexity since starving threads and priority inversion (prioritizing a lower priority thread) can both lead to serious problems.
     179
     180An important observation is that threads do not need to have explicit priorities for problems to occur.
     181Indeed, any system with multiple ready queues that attempts to exhaust one queue before accessing the other queues, essentially provides implicit priority, which can encounter starvation problems.
     182For example, a popular scheduling strategy that suffers from implicit priorities is work stealing.
     183\newterm{Work stealing} is generally presented as follows:
     184\begin{enumerate}
     185        \item Each processor has a list of ready threads.
     186        \item Each processor runs threads from its ready queue first.
     187        \item If a processor's ready queue is empty, attempt to run threads from some other processor's ready queue.
     188\end{enumerate}
     189In a loaded system\footnote{A \newterm{loaded system} is a system where threads are being run at the same rate they are scheduled.}, if a thread does not yield, block, or preempt for an extended period of time, threads on the same processor's list starve if no other processors exhaust their list.
     190
     191Since priorities can be complex for programmers to incorporate into their execution intuition, the \CFA scheduling strategy does not provided explicit priorities and attempts to eliminate implicit priorities.
     192
     193\subsection{Schedulers without feedback or priorities}
     194This proposal conjectures that it is possible to construct a default scheduler for the \CFA runtime that offers good scalability and a simple fairness guarantee that is easy for programmers to reason about.
     195The simplest fairness guarantee is FIFO ordering, \ie threads scheduled first run first.
     196However, enforcing FIFO ordering generally conflicts with scalability across multiple processors because of the additional synchronization.
     197Thankfully, strict FIFO is not needed for sufficient fairness.
     198Since concurrency is inherently non-deterministic, fairness concerns in scheduling are only a problem if a thread repeatedly runs before another thread can run.
     199Some relaxation is possible because non-determinism means programmers already handle ordering problems to produce correct code and hence rely on weak guarantees, \eg that a thread \emph{eventually} runs.
     200Since some reordering does not break correctness, the FIFO fairness guarantee can be significantly relaxed without causing problems.
     201For this proposal, the target guarantee is that the \CFA scheduler provides \emph{probable} FIFO ordering, which allows reordering but makes it improbable that threads are reordered far from their position in total ordering.
     202
     203The \CFA scheduler fairness is defined as follows:
     204\begin{quote}
     205Given two threads $X$ and $Y$, the odds that thread $X$ runs $N$ times \emph{after} thread $Y$ is scheduled but \emph{before} it is run, decreases exponentially with regard to $N$.
     206\end{quote}
     207While this is not a bounded guarantee, the probability that unfairness persist for long periods of times decreases exponentially, making persisting unfairness virtually impossible.
     208
     209% ===============================================================================
     210% ===============================================================================
     211\section{Proposal Details}
     212
     213\subsection{Central Ready Queue} \label{sec:queue}
     214A central ready queue can be built from a FIFO queue, where user threads are pushed onto the queue when they are ready to run, and processors (kernel-threads acting as virtual processors) pop the user threads from the queue and execute them.
     215Alistarh \etal~\cite{alistarh2018relaxed} show it is straightforward to build a relaxed FIFO list that is fast and scalable for loaded or overloaded systems.
     216The described queue uses an array of underlying strictly FIFO queues as shown in Figure~\ref{fig:base}\footnote{For this section, the number of underlying queues is assumed to be constant.
     217Section~\ref{sec:resize} discusses resizing the array.}.
     218Pushing new data is done by selecting one of the underlying queues at random, recording a timestamp for the operation, and pushing to the selected queue.
     219Popping is done by selecting two queues at random and popping from the queue with the oldest timestamp.
     220A higher number of underlying queues leads to less contention on each queue and therefore better performance.
     221In a loaded system, it is highly likely the queues are non-empty, \ie several threads are on each of the underlying queues.
     222For this case, selecting a queue at random to pop from is highly likely to yield a queue with available items.
     223In Figure~\ref{fig:base}, ignoring the ellipsis, the chances of getting an empty queue is 2/7 per pick, meaning two random picks yield an item approximately 9 times out of 10.
     224
     225\begin{figure}
     226        \begin{center}
     227                \input{base.pstex_t}
     228        \end{center}
     229        \caption{Loaded relaxed FIFO list base on an array of strictly FIFO lists.
     230        A timestamp appears in each node and array cell.}
     231        \label{fig:base}
     232\end{figure}
     233
     234\begin{figure}
     235        \begin{center}
     236                \input{empty.pstex_t}
     237        \end{center}
     238        \caption{Underloaded relaxed FIFO list where the array contains many empty cells.}
     239        \label{fig:empty}
     240\end{figure}
     241
     242In an underloaded system, several of the queues are empty, so selecting a random queue for popping is less likely to yield a successful selection and more attempts are needed, resulting in a performance degradation.
     243Figure~\ref{fig:empty} shows an example with fewer elements, where the chances of getting an empty queue is 5/7 per pick, meaning two random picks yield an item only half the time.
     244Since the ready queue is not empty, the pop operation \emph{must} find an element before returning and therefore must retry.
     245Note, the popping kernel thread has no work to do, but CPU cycles are wasted both for available user and kernel threads during the pop operation as the popping thread is using a CPU.
     246Overall performance is therefore influenced by the contention on the underlying queues and pop performance is influenced by the item density.
     247
     248This leads to four performance cases for the centralized ready queue, as depicted in Table~\ref{tab:perfcases}.
     249The number of processors (many or few) refers to the number of kernel threads \emph{actively} attempting to pop user threads from the queues, not the total number of kernel threads.
     250The number of threads (many or few) refers to the number of user threads ready to be run.
     251Many threads means they outnumber processors significantly and most underlying queues have items, few threads mean there are barely more threads than processors and most underlying queues are empty.
     252Cases with fewer threads than processors are discussed in Section~\ref{sec:sleep}.
     253
     254\begin{table}
     255        \begin{center}
     256                \begin{tabular}{|r|l|l|}
     257                        \cline{2-3}
     258                        \multicolumn{1}{r|}{} & \multicolumn{1}{c|}{Many Processors} & \multicolumn{1}{c|}{Few Processors} \\
     259                        \hline
     260                        Many Threads & A: good performance & B: good performance \\
     261                        \hline
     262                        Few Threads  & C: worst performance & D: poor performance \\
     263                        \hline
     264                \end{tabular}
     265        \end{center}
     266        \caption{Expected performance of the relaxed FIFO list in different cases.}
     267        \label{tab:perfcases}
     268\end{table}
     269
     270Performance can be improved in Table~\ref{tab:perfcases} case~D by adding information to help processors find which inner queues are used.
     271This addition aims to avoid the cost of retrying the pop operation but does not affect contention on the underlying queues and can incur some management cost for both push and pop operations.
     272The approach used to encode this information can vary in density and be either global or local.
     273\newterm{Density} means the information is either packed in a few cachelines or spread across several cachelines, and \newterm{local information} means each thread uses an independent copy instead of a single global, \ie common, source of information.
     274
     275For example, Figure~\ref{fig:emptybit} shows a dense bitmask to identify which inner queues are currently in use.
     276This approach means processors can often find user threads in constant time, regardless of how many underlying queues are empty.
     277Furthermore, modern x86 CPUs have extended bit manipulation instructions (BMI2) that allow using the bitmask with very little overhead compared to the randomized selection approach for a filled ready queue, offering good performance even in cases with many empty inner queues.
     278However, this technique has its limits: with a single word\footnote{Word refers here to however many bits can be written atomically.} bitmask, the total number of underlying queues in the ready queue is limited to the number of bits in the word.
     279With a multi-word bitmask, this maximum limit can be increased arbitrarily, but it is not possible to check if the queue is empty by reading the bitmask atomically.
     280
     281Finally, a dense bitmap, either single or multi-word, causes additional problems in Table~\ref{tab:perfcases} case C, because many processors are continuously scanning the bitmask to find the few available threads.
     282This increased contention on the bitmask(s) reduces performance because of cache misses after updates and the bitmask is updated more frequently by the scanning processors racing to read and/or update that information.
     283This increased update frequency 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 user threads but none on queue.
     284
     285\begin{figure}
     286        \begin{center}
     287                {\resizebox{0.73\textwidth}{!}{\input{emptybit}}}
     288        \end{center}
     289        \vspace*{-5pt}
     290        \caption{Underloaded queue with added bitmask to indicate which array cells have items.}
     291        \label{fig:emptybit}
     292        \begin{center}
     293                {\resizebox{0.73\textwidth}{!}{\input{emptytree}}}
     294        \end{center}
     295        \vspace*{-5pt}
     296        \caption{Underloaded queue with added binary search tree indicate which array cells have items.}
     297        \label{fig:emptytree}
     298        \begin{center}
     299                {\resizebox{0.9\textwidth}{!}{\input{emptytls}}}
     300        \end{center}
     301        \vspace*{-5pt}
     302        \caption{Underloaded queue with added per processor bitmask to indicate which array cells have items.}
     303        \label{fig:emptytls}
     304\end{figure}
     305
     306Figure~\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}.
     307However, this approach may lead to poorer performance in Table~\ref{tab:perfcases} case~B due to the inherent pointer chasing cost and already low contention cost in that case.
     308
     309Figure~\ref{fig:emptytls} shows an approach using dense information, similar to the bitmap, but have each thread keep its own independent copy of it.
     310While this approach can offer good scalability \emph{and} low latency, the liveliness of the information can become a problem.
     311In the simple cases, local copies can become stale and end-up not being useful for the pop operation.
     312A more serious problem is that reliable information is necessary for some parts of this algorithm to be correct.
     313As mentioned in this section, processors must know \emph{reliably} whether the list is empty or not to decide if they can return \texttt{NULL} or if they must keep looking during a pop operation.
     314Section~\ref{sec:sleep} discusses another case where reliable information is required for the algorithm to be correct.
     315
     316There is a fundamental tradeoff among these approach.
     317Dense global information about empty underlying queues helps zero-contention cases at the cost of the high-contention case.
     318Sparse global information helps high-contention cases but increases latency in zero-contention cases to read and ``aggregate'' the information\footnote{Hierarchical structures, \eg binary search tree, effectively aggregate information but follow pointer chains, learning information at each node.
     319Similarly, other sparse schemes need to read multiple cachelines to acquire all the information needed.}.
     320Finally, dense local information has both the advantages of low latency in zero-contention cases and scalability in high-contention cases.
     321However, the information can become stale making it difficult to use to ensure correctness.
     322The fact that these solutions have these fundamental limits suggest to me a better solution that attempts to combine these properties in an interesting way.
     323Also, the lock discussed in Section~\ref{sec:resize} allows for solutions that adapt to the number of processors, which could also prove useful.
    126324
    127325\paragraph{Objectives and Existing Work}
    128 How much scalability is actually needed is highly debatable, libfibre\cit is has compared favorably to other schedulers in webserver tests\cit and uses a single atomic counter in its scheduling algorithm similarly to the proposed bitmask. As such the single atomic instruction on a shared cacheline may be sufficiently performant.
    129 
    130 I have built a prototype of this ready-queue (including the bitmask and BMI2 usage, but not the sharded bitmask) and ran performance experiments on it but it is difficult to compare this prototype to a thread scheduler as the prototype is used as a data-queue. I have also integrated this prototype into the \CFA runtime, but have not yet created performance experiments to compare results. I believe that the bitmask approach is currently one of the larger risks of the proposal, early tests lead me to believe it may work but it is not clear that the contention problem can be overcome. The worst-case scenario is a case where the number of processors and the number of ready threads are similar, yet scheduling events are very frequent. Fewer threads should lead to the Idle Sleep mechanism reducing contention while having many threads ready leads to optimal performance. It is difficult to evaluate the likeliness of this worst-case scenario in real workloads. I believe, frequent scheduling events suggest a more ``bursty'' workload where new work is finely divided among many threads which race to completion. This type of workload would only see a peek of contention close to the end of the work, but no sustained contention. Very fine-grained pipelines are less ``bursty'', these may lead to more sustained contention. However, they could also easily benefit from a direct hand-off strategy which would circumvent the problem entirely.
    131 
    132 \subsection{Dynamic Resizing}
    133 The \CFA runtime system currently handles dynamically adding and removing processors from clusters at any time. Since this is part of the existing design, the proposed scheduler must also support this behaviour. However, dynamicly resizing the clusters is considered a rare event associated with setup, teardown and major configuration changes. This assumptions is made both in the design of the proposed scheduler as well as in the original design of the \CFA runtime system. As such, the proposed scheduler must honor the correctness of these behaviour but does not have any performance objectives with regards to resizing a cluster. How long adding or removing processors take and how much this disrupts the performance of other threads is considered a secondary concern since it should be amortized over long period of times. This description effectively matches with te description of a Reader-Writer lock, in frequent but invasive updates among frequent (mostly) read operations. In the case of the Ready-Queue described above, read operations are operations that push or pop from the ready-queue but do not invalidate any references to the ready queue data structures. Writes on the other-hand would add or remove inner queues, invalidating references to the array of inner queues in the process. Therefore, the current proposed approach to this problem is the add a per-cluster Reader Writer lock around the ready queue to prevent restructuring of the ready-queue data structure while threads are being pushed or popped.
    134 
    135 There are possible alternatives to the Reader Writer lock solution. This problem is effectively a memory reclamation problem and as such there is a large body of research on the subject. However, the RWlock solution is simple and can be leveraged to solve other problems (e.g. processor ordering and memory reclamation of threads) which makes it an attractive solution.
     326
     327How much scalability is actually needed is highly debatable.
     328\emph{libfibre}~\cite{libfibre} has compared favourably to other schedulers in webserver tests~\cite{Karsten20} and uses a single atomic counter in its scheduling algorithm similarly to the proposed bitmask.
     329As such, the single atomic instruction on a shared cacheline may be sufficiently performant.
     330
     331I have built a prototype of this ready queue in the shape of a data queue, \ie nodes on the queue are structures with a single $int$ representing a thread and intrusive data fields.
     332Using this prototype, preliminary performance experiments confirm the expected performance in Table~\ref{tab:perfcases}.
     333However, these experiments only offer a hint at the actual performance of the scheduler since threads are involved in more complex operations, \eg threads are not independent of each other: when a thread blocks some other thread must intervene to wake it.
     334
     335I have also integrated this prototype into the \CFA runtime, but have not yet created performance experiments to compare results, as creating one-to-one comparisons between the prototype and the \CFA runtime will be complex.
     336
     337\subsection{Dynamic Resizing} \label{sec:resize}
     338
     339\begin{figure}
     340        \begin{center}
     341                \input{system.pstex_t}
     342        \end{center}
     343        \caption{Global structure of the \CFA runtime system.}
     344        \label{fig:system}
     345\end{figure}
     346
     347The \CFA runtime system groups processors together as \newterm{clusters}, as shown in Figure~\ref{fig:system}.
     348Threads on a cluster are always scheduled on one of the processors of the cluster.
     349Currently, the runtime handles dynamically adding and removing processors from clusters at any time.
     350Since this feature is part of the existing design, the proposed scheduler must also support this behaviour.
     351However, dynamically resizing a cluster is considered a rare event associated with setup, tear down and major configuration changes.
     352This assumption is made both in the design of the proposed scheduler as well as in the original design of the \CFA runtime system.
     353As such, the proposed scheduler must honour the correctness of this behaviour but does not have any performance objectives with regard to resizing a cluster.
     354That is, the time to add or remove processors and how much this disrupts the performance of other threads is considered a secondary concern since it should be amortized over long periods of times.
     355However, as mentioned in Section~\ref{sec:queue}, contention on the underlying queues can have a direct impact on performance.
     356The number of underlying queues must therefore be adjusted as the number of processors grows or shrinks.
     357Since the underlying queues are stored in a dense array, changing the number of queues requires resizing the array and expanding the array requires moving it, which can introduce memory reclamation problems if not done correctly.
     358
     359\begin{figure}
     360        \begin{center}
     361                \input{resize}
     362        \end{center}
     363        \caption{Copy of data structure shown in Figure~\ref{fig:base}.}
     364        \label{fig:base2}
     365\end{figure}
     366
     367It is important to note how the array is used in this case.
     368While the array cells are modified by every push and pop operation, the array itself, \ie the pointer that would change when resized, is only read during these operations.
     369Therefore the use of this pointer can be described as frequent reads and infrequent writes.
     370This description effectively matches with the description of a reader-writer lock, infrequent but invasive updates among frequent read operations.
     371In the case of the ready queue described above, read operations are operations that push or pop from the ready queue but do not invalidate any references to the ready queue data structures.
     372Writes, on the other hand, would add or remove inner queues, invalidating references to the array of inner queues in a process.
     373Therefore, the current proposed approach to this problem is to add a per-cluster reader-writer lock around the ready queue to prevent restructuring of the ready-queue data-structure while threads are being pushed or popped.
     374
     375There are possible alternatives to the reader-writer lock solution.
     376This problem is effectively a memory reclamation problem and as such there is a large body of research on the subject~\cite{brown2015reclaiming, michael2004hazard}.
     377However, the reader-write lock-solution is simple and can be leveraged to solve other problems (\eg processor ordering and memory reclamation of threads), which makes it an attractive solution.
    136378
    137379\paragraph{Objectives and Existing Work}
    138 The lock must offer scalability and performance on par with the actual ready-queue in order not to introduce a new bottle neck. I have already built a lock that fits the desired requirements and preliminary testing show scalability and performance that exceed the target. As such, I do not consider this lock to be a risk on this project.
    139 
    140 \subsection{Idle Sleep} \label{sleep}
    141 As mentionned above, idle sleep is the process of putting processors to sleep while they do not have threads to execute. In this context processors are kernel-threads and sleeping refers to asking the kernel to block a thread. This can be achieved with either thread synchronization operations like pthread\_cond\_wait or using signal operations like sigsuspend.
    142 
    143 Support for idle sleep broadly involves calling the operating system to block the kernel thread but also handling the race between the sleeping and the waking up, and handling which kernel thread should sleep or wake-up.
    144 
    145 When a processor decides to sleep, there is a race that occurs between it signalling that it will go to sleep (so other processors can find sleeping processors) and actually blocking the kernel thread. This is equivalent to the classic problem of missing signals when using condition variables, the ``sleepy'' processor indicates that it will sleep but has not yet gone to sleep, if another processor attempts to wake it up, the waking-up operation may claim nothing needs to be done and the signal will have been missed. In cases where threads are scheduled from processors on the current cluster, loosing signals is not necessarily critical, because at least some processors on the cluster are awake. Individual processors always finish shceduling threads before looking for new work, which means that the last processor to go to sleep cannot miss threads scheduled from inside the cluster (if they do, that demonstrates the ready-queue is not linearizable). However, this guarantee does not hold if threads are shceduled from outside the cluster, either due to an external event like timers and I/O, or due to a thread migrating from a different cluster. In this case, missed signals can lead to the cluster deadlocking where it should not\footnote{Clusters ``should'' never deadlock, but for this proposal, cases where \CFA users \emph{actually} wrote \CFA code that leads to a deadlock it is considered as a deadlock that ``should'' happen. }. Therefore, it is important that the scheduling of threads include a mechanism where signals \emph{cannot} be missed. For performance reasons, it can be advantageous to have a secondary mechanism that allows signals to be missed in cases where it cannot lead to a deadlock. To be safe, this process must include a ``handshake'' where it is guaranteed that either~: the sleepy processor notices that a thread was scheduled after it signalled its intent to block or code scheduling threads well see the intent to sleep before scheduling and be able to wake-up the processor. This matter is complicated by the fact that pthread offers few tools to implement this solution and offers no guarantee of ordering of threads waking up for most of these tools.
    146 
    147 Another issues is trying to avoid kernel sleeping and waking frequently. A possible partial solution is to order the processors so that the one which most recently went to sleep is woken up. This allows other sleeping processors to reach deeper sleep state (when these are available) while keeping ``hot'' processors warmer. Note that while this generally means organising the processors in a stack, I believe that the unique index provided by the ReaderWriter lock can be reused to strictly order the waking order of processors, causing a LIFO like waking order. While a strict LIFO stack is probably better, using the processor index could proove useful and offer a sufficiently LIFO ordering.
    148 
    149 Finally, another important aspect of Idle Sleep is when should processors make the decision to sleep and when it is appropriate for sleeping processors to be woken up. Processors that are unnecessarily awake lead to unnecessary contention and power consumption, while too many sleeping processors can lead to sub-optimal throughput. Furthermore, transitions from sleeping to awake and vice-versa also add unnecessary latency. There is already a wealth of research on the subject and I do not plan to implement a novel idea for the Idle Sleep heuristic in this project.
     380The lock must offer scalability and performance on par with the actual ready queue in order not to introduce a new bottleneck.
     381I have already built a lock that fits the desired requirements and preliminary testing show scalability and performance that exceed the target.
     382As such, I do not consider this lock to be a risk for this project.
     383
     384\subsection{Idle Sleep} \label{sec:sleep}
     385
     386\newterm{Idle sleep} is the process of putting processors to sleep when they have no threads to execute.
     387In this context, processors are kernel threads and sleeping refers to asking the kernel to block a thread.
     388This operation can be achieved with either thread synchronization operations like $pthread_cond_wait$ or using signal operations like $sigsuspend$.
     389The goal of putting idle processors to sleep is:
     390\begin{enumerate}
     391\item
     392reduce contention on the ready queue, since the otherwise idle processors generally contend trying to pop items from the queue,
     393\item
     394give back unneeded CPU time associated with a process to other user processors executing on the computer,
     395\item
     396and reduce energy consumption in cases where more idle kernel-threads translate into idle CPUs, which can cycle down.
     397\end{enumerate}
     398Support for idle sleep broadly involves calling the operating system to block the kernel thread and handling the race between a blocking thread and the waking thread, and handling which kernel thread should sleep or wake up.
     399
     400When a processor decides to sleep, there is a race that occurs between it signalling that is going to sleep (so other processors can find sleeping processors) and actually blocking the kernel thread.
     401This operation is equivalent to the classic problem of missing signals when using condition variables: the ``sleepy'' processor indicates its intention to block but has not yet gone to sleep when another processor attempts to wake it up.
     402The waking-up operation sees the blocked process and signals it, but the blocking process is racing to sleep so the signal is missed.
     403In cases where kernel threads are managed as processors on the current cluster, losing signals is not necessarily critical, because at least some processors on the cluster are awake and may check for more processors eventually.
     404Individual processors always finish scheduling user threads before looking for new work, which means that the last processor to go to sleep cannot miss threads scheduled from inside the cluster (if they do, that demonstrates the ready queue is not linearizable).
     405However, this guarantee does not hold if threads are scheduled from outside the cluster, either due to an external event like timers and I/O, or due to a user (or kernel) thread migrating from a different cluster.
     406In this case, missed signals can lead to the cluster deadlocking\footnote{Clusters should only deadlock in cases where a \CFA programmer \emph{actually} writes \CFA code that leads to a deadlock.}.
     407Therefore, it is important that the scheduling of threads include a mechanism where signals \emph{cannot} be missed.
     408For performance reasons, it can be advantageous to have a secondary mechanism that allows signals to be missed in cases where it cannot lead to a deadlock.
     409To be safe, this process must include a ``handshake'' where it is guaranteed that either:
     410\begin{enumerate}
     411\item
     412the sleeping processor notices that a user thread is scheduled after the sleeping processor signalled its intent to block or
     413\item
     414code scheduling threads sees the intent to sleep before scheduling and be able to wake-up the processor.
     415\end{enumerate}
     416This matter is complicated by the fact that pthreads and Linux offer few tools to implement this solution and no guarantee of ordering of threads waking up for most of these tools.
     417
     418Another important issue is avoiding kernel threads sleeping and waking frequently because there is a significant operating-system cost.
     419This scenario happens when a program oscillates between high and low activity, needing most and then few processors.
     420A possible partial solution is to order the processors so that the one which most recently went to sleep is woken up.
     421This allows other sleeping processors to reach deeper sleep state (when these are available) while keeping ``hot'' processors warmer.
     422Note that while this generally means organizing the processors in a stack, I believe that the unique index provided in my reader-writer lock can be reused to strictly order the waking processors, causing a mostly LIFO order.
     423While a strict LIFO stack is probably better, the processor index could prove useful for other reasons, while still offering a sufficiently LIFO ordering.
     424
     425A final important aspect of idle sleep is when should processors make the decision to sleep and when is it appropriate for sleeping processors to be woken up.
     426Processors that are unnecessarily unblocked lead to unnecessary contention, CPU usage, and power consumption, while too many sleeping processors can lead to suboptimal throughput.
     427Furthermore, transitions from sleeping to awake and vice versa also add unnecessary latency.
     428There is already a wealth of research on the subject~\cite{schillings1996engineering, wiki:thunderherd} and I may use an existing approach for the idle-sleep heuristic in this project, \eg~\cite{Karsten20}.
    150429
    151430\subsection{Asynchronous I/O}
    152 The final aspect of this proposal is asynchronous I/O. Without it, user threads that execute I/O operations will block the underlying kernel thread. This leads to poor throughput, it would be preferrable to block the user-thread and reuse the underlying kernel-thread to run other ready threads. This requires intercepting the user-threads' calls to I/O operations, redirecting them to an asynchronous I/O interface and handling the multiplexing between the synchronous and asynchronous API. As such, these are the three components needed to implemented to support asynchronous I/O : an OS abstraction layer over the asynchronous interface, an event-engine to (de)multiplex the operations and a synchronous interface for users to use. None of these components currently exist in \CFA and I will need to build all three for this project.
    153 
    154 \paragraph{OS Abstraction}
    155 One of the fundamental part of this converting blocking I/O operations into non-blocking ones. This relies on having an underlying asynchronous I/O interface to which to direct the I/O operations. While there exists many different APIs for asynchronous I/O, it is not part of this proposal to create a novel API, simply to use an existing one that is sufficient. uC++ uses the \texttt{select} as its interface, which handles pipes and sockets. It entails significant complexity and has performances problems which make it a less interesting alternative. Another interface which is becoming popular recently\cit is \texttt{epoll}. However, epoll also does not handle file system and seems to have problem to linux pipes and \texttt{TTY}s\cit. A very recent alternative that must still be investigated is \texttt{io\_uring}. It claims to address some of the issues with \texttt{epoll} but is too recent to be confident that it does. Finally, a popular cross-platform alternative is \texttt{libuv}, which offers asynchronous sockets and asynchronous file system operations (among other features). However, as a full-featured library it includes much more than what is needed and could conflict with other features of \CFA unless significant efforts are made to merge them together.
    156 
    157 \paragraph{Event-Engine}
    158 Laying on top of the asynchronous interface layer is the event-engine. This engine is responsible for multiplexing (batching) the synchronous I/O requests into an asynchronous I/O request and demultiplexing the results onto appropriate blocked threads. This can be straightforward for the simple cases, but can become quite complex. Decisions that will need to be made include : whether to poll from a seperate kernel thread or a regularly scheduled user thread, what should be the ordering used when results satisfy many requests, how to handle threads waiting for multiple operations, etc.
     431
     432The final aspect of this proposal is asynchronous I/O.
     433Without it, user threads that execute I/O operations block the underlying kernel thread, which leads to poor throughput.
     434It is preferable to block the user thread performing the I/O and reuse the underlying kernel-thread to run other ready user threads.
     435This approach requires intercepting user-thread calls to I/O operations, redirecting them to an asynchronous I/O interface, and handling the multiplexing/demultiplexing between the synchronous and asynchronous API.
     436As such, there are three components needed to implement support for asynchronous I/O:
     437\begin{enumerate}
     438\item
     439an OS abstraction layer over the asynchronous interface,
     440\item
     441an event-engine to (de)multiplex the operations,
     442\item
     443and a synchronous interface for users.
     444\end{enumerate}
     445None of these components currently exist in \CFA and I will need to build all three for this project.
     446
     447\paragraph{OS Asynchronous Abstraction}
     448One fundamental part for converting blocking I/O operations into non-blocking is having an underlying asynchronous I/O interface to direct the I/O operations.
     449While there exists many different APIs for asynchronous I/O, it is not part of this proposal to create a novel API.
     450It is sufficient to make one work in the complex context of the \CFA runtime.
     451\uC uses the $select$~\cite{select} as its interface, which handles ttys, pipes and sockets, but not disk.
     452$select$ entails significant complexity and is being replaced in UNIX operating systems, which make it a less interesting alternative.
     453Another popular interface is $epoll$~\cite{epoll}, which is supposed to be cheaper than $select$.
     454However, $epoll$ also does not handle the file system and anecdotal evidence suggest it has problems with Linux pipes and ttys.
     455A popular cross-platform alternative is $libuv$~\cite{libuv}, which offers asynchronous sockets and asynchronous file system operations (among other features).
     456However, as a full-featured library it includes much more than I need and could conflict with other features of \CFA unless significant effort is made to merge them together.
     457A very recent alternative that I am investigating is $io_uring$~\cite{io_uring}.
     458It claims to address some of the issues with $epoll$ and my early investigating suggests that the claim is accurate.
     459$io_uring$ uses a much more general approach where system calls are registered to a queue and later executed by the kernel, rather than relying on system calls to support returning an error instead of blocking.
     460I believe this approach allows for fewer problems, \eg the manpage for $open$~\cite{open} states:
     461\begin{quote}
     462Note that [the $O_NONBLOCK$ flag] has no effect for regular files and block devices;
     463that is, I/O operations will (briefly) block when device activity is required, regardless of whether $O_NONBLOCK$ is set.
     464Since $O_NONBLOCK$ semantics might eventually be implemented, applications should not depend upon blocking behaviour when specifying this flag for regular files and block devices.
     465\end{quote}
     466This makes approaches based on $select$/$epoll$ less reliable since they may not work for every file descriptors.
     467For this reason, I plan to use $io_uring$ as the OS abstraction for the \CFA runtime unless further work encounters a fatal problem.
     468However, only a small subset of the features are available in Ubuntu as of April 2020~\cite{wiki:ubuntu-linux}, which will limit performance comparisons.
     469I do not believe this will affect the comparison result.
     470
     471\paragraph{Event Engine}
     472Above the OS asynchronous abstraction is the event engine.
     473This engine is responsible for multiplexing (batching) the synchronous I/O requests into asynchronous I/O requests and demultiplexing the results to appropriate blocked user threads.
     474This step can be straightforward for simple cases, but becomes quite complex when there are thousands of user threads performing both reads and writes, possibly on overlapping file descriptors.
     475Decisions that need to be made include:
     476\begin{enumerate}
     477\item
     478whether to poll from a separate kernel thread or a regularly scheduled user thread,
     479\item
     480what should be the ordering used when results satisfy many requests,
     481\item
     482how to handle threads waiting for multiple operations, etc.
     483\end{enumerate}
    159484
    160485\paragraph{Interface}
    161 Finally, for these components to be available, it is necessary to expose them through a synchronous interface. This can be a novel interface but it is preferrable to attempt to intercept the existing POSIX interface in order to be compatible with existing code. This will allow C programs written using this interface to be transparently converted to \CFA with minimal effeort. Where this is not applicable, a novel interface will be created to fill the gaps.
     486Finally, for these non-blocking I/O components to be available, it is necessary to expose them through a synchronous interface because that is the \CFA concurrent programming style.
     487The interface can be novel but it is preferable to match the existing POSIX interface when possible to be compatible with existing code.
     488Matching allows C programs written using this interface to be transparently converted to \CFA with minimal effort.
     489Where new functionality is needed, I will add novel interface extensions to fill gaps and provide advanced features.
    162490
    163491
     
    165493% ===============================================================================
    166494\section{Discussion}
    167 
     495I believe that runtime system and scheduling are still open topics.
     496Many ``state of the art'' production frameworks still use single-threaded event loops because of performance considerations, \eg~\cite{nginx-design}, and, to my knowledge, no widely available system language offers modern threading facilities.
     497I believe the proposed work offers a novel runtime and scheduling package, where existing work only offers fragments that users must assemble themselves when possible.
    168498
    169499% ===============================================================================
    170500% ===============================================================================
    171501\section{Timeline}
    172 
    173 
    174 \cleardoublepage
     502\begin{center}
     503\begin{tabular}{ | r @{--} l | p{4in} | }
     504\hline May 2020 & October 2020   & Creation of the performance benchmark. \\
     505\hline November 2020 & March 2021   & Completion of the implementation. \\
     506\hline March 2021 & April 2021  & Final Performance experiments. \\
     507\hline May 2021 & August 2021 & Thesis writing and defence. \\
     508\hline
     509\end{tabular}
     510\end{center}
    175511
    176512% B I B L I O G R A P H Y
    177513% -----------------------------
    178 \addcontentsline{toc}{chapter}{Bibliography}
     514\cleardoublepage
     515\phantomsection         % allows hyperref to link to the correct page
     516\addcontentsline{toc}{section}{\refname}
    179517\bibliographystyle{plain}
    180518\bibliography{pl,local}
     519
     520% G L O S S A R Y
     521% -----------------------------
    181522\cleardoublepage
    182523\phantomsection         % allows hyperref to link to the correct page
    183 
    184 % G L O S S A R Y
    185 % -----------------------------
    186 \addcontentsline{toc}{chapter}{Glossary}
     524\addcontentsline{toc}{section}{Glossary}
    187525\printglossary
    188 \cleardoublepage
    189 \phantomsection         % allows hyperref to link to the correct page
    190526
    191527\end{document}
  • doc/theses/thierry_delisle_PhD/comp_II/local.bib

    r3c64c668 r58fe85a  
    7676
    7777@article{finkel1987dib,
    78   title={DIB—a distributed implementation of backtracking},
     78  title={DIB-a distributed implementation of backtracking},
    7979  author={Finkel, Raphael and Manber, Udi},
    8080  journal={ACM Transactions on Programming Languages and Systems (TOPLAS)},
     
    221221  organization={ACM}
    222222}
     223
     224% ===============================================================================
     225% Algorithms
     226% ===============================================================================
     227@article{michael2004hazard,
     228  title={Hazard pointers: Safe memory reclamation for lock-free objects},
     229  author={Michael, Maged M},
     230  journal={IEEE Transactions on Parallel and Distributed Systems},
     231  volume={15},
     232  number={6},
     233  pages={491--504},
     234  year={2004},
     235  publisher={IEEE}
     236}
     237
     238@inproceedings{brown2015reclaiming,
     239  title={Reclaiming memory for lock-free data structures: There has to be a better way},
     240  author={Brown, Trevor Alexander},
     241  booktitle={Proceedings of the 2015 ACM Symposium on Principles of Distributed Computing},
     242  pages={261--270},
     243  year={2015}
     244}
     245
     246% Trevor's relaxed FIFO list
     247@inproceedings{alistarh2018relaxed,
     248  title={Relaxed schedulers can efficiently parallelize iterative algorithms},
     249  author={Alistarh, Dan and Brown, Trevor and Kopinsky, Justin and Nadiradze, Giorgi},
     250  booktitle={Proceedings of the 2018 ACM Symposium on Principles of Distributed Computing},
     251  pages={377--386},
     252  year={2018}
     253}
     254
     255% Scalable counters which only support is !0
     256@inproceedings{ellen2007snzi,
     257  title={SNZI: Scalable nonzero indicators},
     258  author={Ellen, Faith and Lev, Yossi and Luchangco, Victor and Moir, Mark},
     259  booktitle={Proceedings of the twenty-sixth annual ACM symposium on Principles of distributed computing},
     260  pages={13--22},
     261  year={2007}
     262}
     263
     264% ===============================================================================
     265% Linux Man Pages
     266% ===============================================================================
     267@manual{open,
     268  key        = "open",
     269  title      = "open(2) Linux User's Manual",
     270  year       = "2020",
     271  month      = "February",
     272}
     273
     274@manual{epoll,
     275  key        = "epoll",
     276  title      = "epoll(7) Linux User's Manual",
     277  year       = "2019",
     278  month      = "March",
     279}
     280
     281@manual{select,
     282  key        = "select",
     283  title      = "select(7) Linux User's Manual",
     284  year       = "2019",
     285  month      = "March",
     286}
     287
     288@misc{io_uring,
     289  title   = {Efficient IO with io\_uring},
     290  author  = {Axboe, Jens},
     291  year    = "2019",
     292  month   = "March",
     293  version = {0,4},
     294  howpublished = {\url{https://kernel.dk/io_uring.pdf}}
     295}
     296
     297@misc{libuv,
     298  key   = "libuv",
     299  title = {libuv},
     300  howpublished = {\url{https://github.com/libuv/libuv}}
     301}
     302
     303% ===============================================================================
     304% MISC
     305% ===============================================================================
     306
     307@misc{nginx-design,
     308  key   = "nginx",
     309  title={Inside {NGINX}: How We Designed for Performance \& Scale},
     310  howpublished= {\href{https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale}
     311                {https://\-www.nginx.com/\-blog/\-inside\--nginx\--how\--we\--designed\--for\--performance\--scale}},
     312}
     313
     314@article{schillings1996engineering,
     315  title={Be engineering insights: Benaphores},
     316  author={Schillings, Benoit},
     317  journal={Be Newsletters},
     318  volume={1},
     319  number={26},
     320  year={1996}
     321}
     322
     323@misc{wiki:thunderherd,
     324   author = "{Wikipedia contributors}",
     325   title = "Thundering herd problem --- {W}ikipedia{,} The Free Encyclopedia",
     326   year = "2020",
     327   howpublished = {\href{https://en.wikipedia.org/wiki/Thundering_herd_problem}
     328                  {https://\-en.wikipedia.org/\-wiki/\-Thundering\_herd\_problem}},},
     329   note = "[Online; accessed 14-April-2020]"
     330}
     331
     332@misc{wiki:ubuntu-linux,
     333   author = "{Wikipedia contributors}",
     334   title = "Ubuntu version history : Table of versions --- {W}ikipedia{,} The Free Encyclopedia",
     335   year = "2020",
     336   howpublished = {\href{https://en.wikipedia.org/wiki/Ubuntu_version_history\#Table_of_versions}
     337                  {https://\-en.wikipedia.org/\-wiki/\-Ubuntu\_version\_history\#Table\_of\_versions}},
     338   note = "[Online; accessed 15-April-2020]"
     339}
  • doc/user/Makefile

    r3c64c668 r58fe85a  
    5555
    5656${DOCUMENT} : ${BASE}.ps
    57         ps2pdf $<
     57        ps2pdf -dPDFSETTINGS=/prepress $<
    5858
    5959${BASE}.ps : ${BASE}.dvi
  • doc/user/user.tex

    r3c64c668 r58fe85a  
    1111%% Created On       : Wed Apr  6 14:53:29 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sat Jul 13 18:36:18 2019
    14 %% Update Count     : 3876
     13%% Last Modified On : Mon Oct  5 08:57:29 2020
     14%% Update Count     : 3998
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    3030\usepackage{upquote}                                                                    % switch curled `'" to straight
    3131\usepackage{calc}
    32 \usepackage{xspace}
    3332\usepackage{varioref}                                                                   % extended references
    34 \usepackage{listings}                                                                   % format program code
     33\usepackage[labelformat=simple,aboveskip=0pt,farskip=0pt]{subfig}
     34\renewcommand{\thesubfigure}{\alph{subfigure})}
    3535\usepackage[flushmargin]{footmisc}                                              % support label/reference in footnote
    3636\usepackage{latexsym}                                   % \Box glyph
    3737\usepackage{mathptmx}                                   % better math font with "times"
    3838\usepackage[usenames]{color}
    39 \input{common}                                          % common CFA document macros
    40 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}
    41 \usepackage{breakurl}
    42 
    43 \usepackage[pagewise]{lineno}
    44 \renewcommand{\linenumberfont}{\scriptsize\sffamily}
    45 \usepackage[firstpage]{draftwatermark}
    46 \SetWatermarkLightness{0.9}
    47 
    48 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore
    49 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR
    50 % AFTER HYPERREF.
    51 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}
    52 
    53 \setlength{\topmargin}{-0.45in}                                                 % move running title into header
    54 \setlength{\headsep}{0.25in}
    55 
    56 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    57 
    58 \CFAStyle                                                                                               % use default CFA format-style
    59 \lstnewenvironment{C++}[1][]                            % use C++ style
    60 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}}
    61 {}
    62 
     39\newcommand{\CFALatin}{}
    6340% inline code Ā©...Ā© (copyright symbol) emacs: C-q M-)
    6441% red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-.
     
    6845% keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^
    6946% math escape $...$ (dollar symbol)
     47\input{common}                                          % common CFA document macros
     48\usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}
     49\usepackage{breakurl}
     50
     51\renewcommand\footnoterule{\kern -3pt\rule{0.3\linewidth}{0.15pt}\kern 2pt}
     52
     53\usepackage[pagewise]{lineno}
     54\renewcommand{\linenumberfont}{\scriptsize\sffamily}
     55\usepackage[firstpage]{draftwatermark}
     56\SetWatermarkLightness{0.9}
     57
     58% Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore
     59% removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR
     60% AFTER HYPERREF.
     61\renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}
     62
     63\setlength{\topmargin}{-0.45in}                                                 % move running title into header
     64\setlength{\headsep}{0.25in}
     65
     66%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     67
     68\CFAStyle                                                                                               % use default CFA format-style
     69\lstnewenvironment{C++}[1][]                            % use C++ style
     70{\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}}
     71{}
     72
     73\newsavebox{\myboxA}
     74\newsavebox{\myboxB}
    7075
    7176%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     
    7984\newcommand{\G}[1]{{\Textbf[OliveGreen]{#1}}}
    8085\newcommand{\KWC}{K-W C\xspace}
    81 
    82 \newsavebox{\LstBox}
    8386
    8487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     
    211214Even with all its problems, C continues to be popular because it allows writing software at virtually any level in a computer system without restriction.
    212215For system programming, where direct access to hardware, storage management, and real-time issues are a requirement, C is usually the only language of choice.
    213 The TIOBE index~\cite{TIOBE} for July 2018 ranks the top five most \emph{popular} programming languages as \Index*{Java} 16\%, C 14\%, \Index*[C++]{\CC{}} 7.5\%, Python 6\%, Visual Basic 4\% = 47.5\%, where the next 50 languages are less than 4\% each, with a long tail.
    214 The top 3 rankings over the past 30 years are:
     216The TIOBE index~\cite{TIOBE} for February 2020 ranks the top six most \emph{popular} programming languages as \Index*{Java} 17.4\%, C 16.8\%, Python 9.3\%, \Index*[C++]{\CC{}} 6.2\%, \Csharp 5.9\%, Visual Basic 5.9\% = 61.5\%, where the next 50 languages are less than 2\% each, with a long tail.
     217The top 4 rankings over the past 35 years are:
    215218\begin{center}
    216219\setlength{\tabcolsep}{10pt}
    217 \begin{tabular}{@{}rccccccc@{}}
    218                 & 2018  & 2013  & 2008  & 2003  & 1998  & 1993  & 1988  \\ \hline
    219 Java    & 1             & 2             & 1             & 1             & 16    & -             & -             \\
    220 \R{C}   & \R{2} & \R{1} & \R{2} & \R{2} & \R{1} & \R{1} & \R{1} \\
    221 \CC             & 3             & 4             & 3             & 3             & 2             & 2             & 5             \\
     220\begin{tabular}{@{}rcccccccc@{}}
     221                & 2020  & 2015  & 2010  & 2005  & 2000  & 1995  & 1990  & 1985  \\ \hline
     222Java    & 1             & 2             & 1             & 2             & 3             & -             & -             & -             \\
     223\R{C}   & \R{2} & \R{1} & \R{2} & \R{1} & \R{1} & \R{2} & \R{1} & \R{1} \\
     224Python  & 3             & 7             & 6             & 6             & 22    & 21    & -             & -             \\
     225\CC             & 4             & 4             & 4             & 3             & 2             & 1             & 2             & 12    \\
    222226\end{tabular}
    223227\end{center}
     
    252256
    253257The signature feature of \CFA is \emph{\Index{overload}able} \Index{parametric-polymorphic} functions~\cite{forceone:impl,Cormack90,Duggan96} with functions generalized using a ©forall© clause (giving the language its name):
    254 \begin{lstlisting}
     258\begin{cfa}
    255259®forall( otype T )® T identity( T val ) { return val; }
    256260int forty_two = identity( 42 ); §\C{// T is bound to int, forty\_two == 42}§
    257 \end{lstlisting}
     261\end{cfa}
    258262% extending the C type system with parametric polymorphism and overloading, as opposed to the \Index*[C++]{\CC{}} approach of object-oriented extensions.
    259263\CFA{}\hspace{1pt}'s polymorphism was originally formalized by \Index*{Glen Ditchfield}\index{Ditchfield, Glen}~\cite{Ditchfield92}, and first implemented by \Index*{Richard Bilson}\index{Bilson, Richard}~\cite{Bilson03}.
     
    274278\begin{comment}
    275279A simple example is leveraging the existing type-unsafe (©void *©) C ©bsearch© to binary search a sorted floating array:
    276 \begin{lstlisting}
     280\begin{cfa}
    277281void * bsearch( const void * key, const void * base, size_t dim, size_t size,
    278282                                int (* compar)( const void *, const void * ));
     
    283287double key = 5.0, vals[10] = { /* 10 sorted floating values */ };
    284288double * val = (double *)bsearch( &key, vals, 10, sizeof(vals[0]), comp ); §\C{// search sorted array}§
    285 \end{lstlisting}
     289\end{cfa}
    286290which can be augmented simply with a polymorphic, type-safe, \CFA-overloaded wrappers:
    287 \begin{lstlisting}
     291\begin{cfa}
    288292forall( otype T | { int ?<?( T, T ); } ) T * bsearch( T key, const T * arr, size_t size ) {
    289293        int comp( const void * t1, const void * t2 ) { /* as above with double changed to T */ }
     
    296300double * val = bsearch( 5.0, vals, 10 ); §\C{// selection based on return type}§
    297301int posn = bsearch( 5.0, vals, 10 );
    298 \end{lstlisting}
     302\end{cfa}
    299303The nested function ©comp© provides the hidden interface from typed \CFA to untyped (©void *©) C, plus the cast of the result.
    300304Providing a hidden ©comp© function in \CC is awkward as lambdas do not use C calling-conventions and template declarations cannot appear at block scope.
     
    304308\CFA has replacement libraries condensing hundreds of existing C functions into tens of \CFA overloaded functions, all without rewriting the actual computations.
    305309For example, it is possible to write a type-safe \CFA wrapper ©malloc© based on the C ©malloc©:
    306 \begin{lstlisting}
     310\begin{cfa}
    307311forall( dtype T | sized(T) ) T * malloc( void ) { return (T *)malloc( sizeof(T) ); }
    308312int * ip = malloc(); §\C{// select type and size from left-hand side}§
    309313double * dp = malloc();
    310314struct S {...} * sp = malloc();
    311 \end{lstlisting}
     315\end{cfa}
    312316where the return type supplies the type/size of the allocation, which is impossible in most type systems.
    313317\end{comment}
     
    512516Keyword clashes are accommodated by syntactic transformations using the \CFA backquote escape-mechanism:
    513517\begin{cfa}
    514 int Ā®`Ā®otypeĀ®`Ā® = 3; §\C{// make keyword an identifier}§
    515 double Ā®`Ā®forallĀ®`Ā® = 3.5;
     518int Ā®``Ā®otype = 3; §\C{// make keyword an identifier}§
     519double Ā®``Ā®forall = 3.5;
    516520\end{cfa}
    517521
     
    524528// include file uses the CFA keyword "with".
    525529#if ! defined( with ) §\C{// nesting ?}§
    526 #define with Ā®`Ā®withĀ®`Ā® §\C{// make keyword an identifier}§
     530#define with Ā®``Ā®with §\C{// make keyword an identifier}§
    527531#define __CFA_BFD_H__
    528532#endif
    529 
    530 Ā®#include_next <bfdlink.h> §\C{// must have internal check for multiple expansion}§
    531 Ā®
     533§{\color{red}\#\textbf{include\_next} <bfdlink.h>}§ §\C{// must have internal check for multiple expansion}§
    532534#if defined( with ) && defined( __CFA_BFD_H__ ) §\C{// reset only if set}§
    533535#undef with
     
    576578\section{Exponentiation Operator}
    577579
    578 C, \CC, and Java (and many other programming languages) have no exponentiation operator\index{exponentiation!operator}\index{operator!exponentiation}, \ie $x^y$, and instead use a routine, like \Indexc{pow}, to perform the exponentiation operation.
    579 \CFA extends the basic operators with the exponentiation operator Ā©?\?Ā©\index{?\\?@Ā©?\?Ā©} and Ā©?\=?Ā©\index{?\\=?@Ā©\=?Ā©}, as in, Ā©x \ yĀ© and Ā©x \= yĀ©, which means $x^y$ and $x \leftarrow x^y$.
     580C, \CC, and Java (and many other programming languages) have no exponentiation operator\index{exponentiation!operator}\index{operator!exponentiation}, \ie $x^y$, and instead use a routine, like \Indexc{pow(x,y)}, to perform the exponentiation operation.
     581\CFA extends the basic operators with the exponentiation operator Ā©?Ā®\Ā®?Ā©\index{?\\?@Ā©?Ā®\Ā®?Ā©} and Ā©?\=?Ā©\index{?\\=?@©®\Ā®=?Ā©}, as in, Ā©x Ā®\Ā® yĀ© and Ā©x Ā®\Ā®= yĀ©, which means $x^y$ and $x \leftarrow x^y$.
    580582The priority of the exponentiation operator is between the cast and multiplicative operators, so that ©w * (int)x \ (int)y * z© is parenthesized as ©((w * (((int)x) \ ((int)y))) * z)©.
    581583
    582 As for \Index{division}, there are exponentiation operators for integral and floating types, including the builtin \Index{complex} types.
     584There are exponentiation operators for integral and floating types, including the builtin \Index{complex} types.
    583585Integral exponentiation\index{exponentiation!unsigned integral} is performed with repeated multiplication\footnote{The multiplication computation is $O(\log y)$.} (or shifting if the exponent is 2).
    584 Overflow from large exponents or negative exponents return zero.
     586Overflow for a large exponent or negative exponent returns zero.
    585587Floating exponentiation\index{exponentiation!floating} is performed using \Index{logarithm}s\index{exponentiation!logarithm}, so the exponent cannot be negative.
    586588\begin{cfa}
     
    5895911 1 256 -64 125 ®0® 3273344365508751233 ®0® ®0® -0.015625 18.3791736799526 0.264715-1.1922i
    590592\end{cfa}
    591 Note, Ā©5 Ā®\Ā® 32Ā© and Ā©5L Ā®\Ā® 64Ā© overflow, and Ā©-4 Ā®\Ā® -3Ā© is a fraction but stored in an integer so all three computations generate an integral zero.
     593Note, Ā©5 \ 32Ā© and Ā©5L \ 64Ā© overflow, and Ā©-4 \ -3Ā© is a fraction but stored in an integer so all three computations generate an integral zero.
    592594Parenthesis are necessary for complex constants or the expression is parsed as ©1.0f+®(®2.0fi \ 3.0f®)®+2.0fi©.
    593595The exponentiation operator is available for all the basic types, but for user-defined types, only the integral-computation version is available.
     
    598600OT ?Ā®\Ā®?( OT ep, unsigned long int y );
    599601\end{cfa}
    600 The user type Ā©TĀ© must define multiplication, one, Ā©1Ā©, and, Ā©*Ā©.
     602The user type Ā©TĀ© must define multiplication, one (Ā©1Ā©), and Ā©*Ā©.
    601603
    602604
     
    626628
    627629
    628 \subsection{Loop Control}
    629 
    630 The Ā©forĀ©/Ā©whileĀ©/Ā©do-whileĀ© loop-control allows empty or simplified ranges (see Figure~\ref{f:LoopControlExamples}).
    631 \begin{itemize}
    632 \item
    633 An empty conditional implies Ā©1Ā©.
    634 \item
    635 The up-to range Ā©~Ā©\index{~@Ā©~Ā©} means exclusive range [M,N).
    636 \item
    637 The up-to range Ā©~=Ā©\index{~=@Ā©~=Ā©} means inclusive range [M,N].
    638 \item
    639 The down-to range Ā©-~Ā©\index{-~@Ā©-~Ā©} means exclusive range [N,M).
    640 \item
    641 The down-to range Ā©-~=Ā©\index{-~=@Ā©-~=Ā©} means inclusive range [N,M].
    642 \item
    643 Ā©@Ā© means put nothing in this field.
    644 \item
    645 Ā©0Ā© is the implicit start value;
    646 \item
    647 Ā©1Ā© is the implicit increment value.
    648 \item
    649 The up-to range uses Ā©+=Ā© for increment;
    650 \item
    651 The down-to range uses Ā©-=Ā© for decrement.
    652 \item
    653 The loop index is polymorphic in the type of the start value or comparison value when start is implicitly Ā©0Ā©.
    654 \end{itemize}
    655 
    656 \begin{figure}
     630%\section{\texorpdfstring{\protect\lstinline@case@ Clause}{case Clause}}
     631\subsection{\texorpdfstring{\LstKeywordStyle{case} Clause}{case Clause}}
     632
     633C restricts the Ā©caseĀ© clause of a Ā©switchĀ© statement to a single value.
     634For multiple Ā©caseĀ© clauses associated with the same statement, it is necessary to have multiple Ā©caseĀ© clauses rather than multiple values.
     635Requiring a Ā©caseĀ© clause for each value does not seem to be in the spirit of brevity normally associated with C.
     636Therefore, the Ā©caseĀ© clause is extended with a list of values, as in:
    657637\begin{cquote}
    658 \begin{tabular}{@{}l|l@{}}
    659 \multicolumn{1}{c|}{loop control} & \multicolumn{1}{c}{output} \\
    660 \hline
    661 \begin{cfa}
    662 sout | nlOff;
    663 while Ā®()Ā® { sout | "empty"; break; } sout | nl;
    664 do { sout | "empty"; break; } while Ā®()Ā®; sout | nl;
    665 for Ā®()Ā® { sout | "empty"; break; } sout | nl;
    666 for ( Ā®0Ā® ) { sout | "A"; } sout | "zero" | nl;
    667 for ( Ā®1Ā® ) { sout | "A"; } sout | nl;
    668 for ( Ā®10Ā® ) { sout | "A"; } sout | nl;
    669 for ( Ā®1 ~= 10 ~ 2Ā® ) { sout | "B"; } sout | nl;
    670 for ( Ā®10 -~= 1 ~ 2Ā® ) { sout | "C"; } sout | nl;
    671 for ( Ā®0.5 ~ 5.5Ā® ) { sout | "D"; } sout | nl;
    672 for ( Ā®5.5 -~ 0.5Ā® ) { sout | "E"; } sout | nl;
    673 for ( Ā®i; 10Ā® ) { sout | i; } sout | nl;
    674 for ( Ā®i; 1 ~= 10 ~ 2Ā® ) { sout | i; } sout | nl;
    675 for ( Ā®i; 10 -~= 1 ~ 2Ā® ) { sout | i; } sout | nl;
    676 for ( Ā®i; 0.5 ~ 5.5Ā® ) { sout | i; } sout | nl;
    677 for ( Ā®i; 5.5 -~ 0.5Ā® ) { sout | i; } sout | nl;
    678 for ( Ā®ui; 2u ~= 10u ~ 2uĀ® ) { sout | ui; } sout | nl;
    679 for ( Ā®ui; 10u -~= 2u ~ 2uĀ® ) { sout | ui; } sout | nl;
    680 enum { N = 10 };
    681 for ( Ā®NĀ® ) { sout | "N"; } sout | nl;
    682 for ( Ā®i; NĀ® ) { sout | i; } sout | nl;
    683 for ( Ā®i; N -~ 0Ā® ) { sout | i; } sout | nl;
    684 const int start = 3, comp = 10, inc = 2;
    685 for ( Ā®i; start ~ comp ~ inc + 1Ā® ) { sout | i; } sout | nl;
    686 for ( Ā®i; 1 ~ @Ā® ) { if ( i > 10 ) break;
    687         sout | i; } sout | nl;
    688 for ( Ā®i; 10 -~ @Ā® ) { if ( i < 0 ) break;
    689         sout | i; } sout | nl;
    690 for ( Ā®i; 2 ~ @ ~ 2Ā® ) { if ( i > 10 ) break;
    691         sout | i; } sout | nl;
    692 for ( Ā®i; 2.1 ~ @ ~ @Ā® ) { if ( i > 10.5 ) break;
    693         sout | i; i += 1.7; } sout | nl;
    694 for ( Ā®i; 10 -~ @ ~ 2Ā® ) { if ( i < 0 ) break;
    695         sout | i; } sout | nl;
    696 for ( Ā®i; 12.1 ~ @ ~ @Ā® ) { if ( i < 2.5 ) break;
    697         sout | i; i -= 1.7; } sout | nl;
    698 for ( Ā®i; 5 : j; -5 ~ @Ā® ) { sout | i | j; } sout | nl;
    699 for ( Ā®i; 5 : j; -5 -~ @Ā® ) { sout | i | j; } sout | nl;
    700 for ( Ā®i; 5 : j; -5 ~ @ ~ 2Ā® ) { sout | i | j; } sout | nl;
    701 for ( Ā®i; 5 : j; -5 -~ @ ~ 2Ā® ) { sout | i | j; } sout | nl;
    702 for ( Ā®j; -5 ~ @ : i; 5Ā® ) { sout | i | j; } sout | nl;
    703 for ( Ā®j; -5 -~ @ : i; 5Ā® ) { sout | i | j; } sout | nl;
    704 for ( Ā®j; -5 ~ @ ~ 2 : i; 5Ā® ) { sout | i | j; } sout | nl;
    705 for ( Ā®j; -5 -~ @ ~ 2 : i; 5Ā® ) { sout | i | j; } sout | nl;
    706 for ( Ā®j; -5 -~ @ ~ 2 : i; 5 : k; 1.5 ~ @Ā® ) {
    707         sout | i | j | k; } sout | nl;
    708 for ( Ā®j; -5 -~ @ ~ 2 : k; 1.5 ~ @ : i; 5Ā® ) {
    709         sout | i | j | k; } sout | nl;
    710 for ( Ā®k; 1.5 ~ @ : j; -5 -~ @ ~ 2 : i; 5Ā® ) {
    711         sout | i | j | k; } sout | nl;
     638\begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{2em}}l@{}}
     639\multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}}        & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{C}} \\
     640\begin{cfa}
     641switch ( i ) {
     642  case Ā®1, 3, 5Ā®:
     643        ...
     644  case Ā®2, 4, 6Ā®:
     645        ...
     646}
    712647\end{cfa}
    713648&
    714649\begin{cfa}
    715 
    716 empty
    717 empty
    718 empty
    719 zero
    720 A
    721 A A A A A A A A A A
    722 B B B B B
    723 C C C C C
    724 D D D D D
    725 E E E E E
    726 0 1 2 3 4 5 6 7 8 9
    727 1 3 5 7 9
    728 10 8 6 4 2
    729 0.5 1.5 2.5 3.5 4.5
    730 5.5 4.5 3.5 2.5 1.5
    731 2 4 6 8 10
    732 10 8 6 4 2
    733 
    734 N N N N N N N N N N
    735 0 1 2 3 4 5 6 7 8 9
    736 10 9 8 7 6 5 4 3 2 1
    737 
    738 3 6 9
    739 
    740 1 2 3 4 5 6 7 8 9 10
    741 
    742 10 9 8 7 6 5 4 3 2 1 0
    743 
    744 2 4 6 8 10
    745 
    746 2.1 3.8 5.5 7.2 8.9
    747 
    748 10 8 6 4 2 0
    749 
    750 12.1 10.4 8.7 7 5.3 3.6
    751 0 -5 1 -4 2 -3 3 -2 4 -1
    752 0 -5 1 -6 2 -7 3 -8 4 -9
    753 0 -5 1 -3 2 -1 3 1 4 3
    754 0 -5 1 -7 2 -9 3 -11 4 -13
    755 0 -5 1 -4 2 -3 3 -2 4 -1
    756 0 -5 1 -6 2 -7 3 -8 4 -9
    757 0 -5 1 -3 2 -1 3 1 4 3
    758 0 -5 1 -7 2 -9 3 -11 4 -13
    759 
    760 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5
    761 
    762 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5
    763 
    764 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5
     650switch ( i ) {
     651  case 1: case 3 : case 5:
     652        ...
     653  case 2: case 4 : case 6:
     654        ...
     655}
     656\end{cfa}
     657&
     658\begin{cfa}
     659
     660// odd values
     661
     662// even values
     663
     664
    765665\end{cfa}
    766666\end{tabular}
    767667\end{cquote}
    768 \caption{Loop Control Examples}
    769 \label{f:LoopControlExamples}
    770 \end{figure}
     668In addition, subranges are allowed to specify case values.\footnote{
     669gcc has the same mechanism but awkward syntax, \lstinline@2 ...42@, because a space is required after a number, otherwise the period is a decimal point.}
     670\begin{cfa}
     671switch ( i ) {
     672  case Ā®1~5:Ā® §\C{// 1, 2, 3, 4, 5}§
     673        ...
     674  case Ā®10~15:Ā® §\C{// 10, 11, 12, 13, 14, 15}§
     675        ...
     676}
     677\end{cfa}
     678Lists of subranges are also allowed.
     679\begin{cfa}
     680case Ā®1~5, 12~21, 35~42Ā®:
     681\end{cfa}
    771682
    772683
     
    977888
    978889
    979 %\section{\texorpdfstring{\protect\lstinline@case@ Clause}{case Clause}}
    980 \subsection{\texorpdfstring{\LstKeywordStyle{case} Statement}{case Statement}}
    981 
    982 C restricts the Ā©caseĀ© clause of a Ā©switchĀ© statement to a single value.
    983 For multiple Ā©caseĀ© clauses associated with the same statement, it is necessary to have multiple Ā©caseĀ© clauses rather than multiple values.
    984 Requiring a Ā©caseĀ© clause for each value does not seem to be in the spirit of brevity normally associated with C.
    985 Therefore, the Ā©caseĀ© clause is extended with a list of values, as in:
    986 \begin{cquote}
    987 \begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{2em}}l@{}}
    988 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}}        & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{C}} \\
    989 \begin{cfa}
    990 switch ( i ) {
    991   case Ā®1, 3, 5Ā®:
     890\subsection{Non-terminating and Labelled \texorpdfstring{\LstKeywordStyle{fallthrough}}{Non-terminating and Labelled fallthrough}}
     891
     892The Ā©fallthroughĀ© clause may be non-terminating within a Ā©caseĀ© clause or have a target label to common code from multiple case clauses.
     893\begin{center}
     894\begin{tabular}{@{}lll@{}}
     895\begin{cfa}
     896choose ( ... ) {
     897  case 3:
     898        if ( ... ) {
     899                ... Ā®fallthru;Ā® // goto case 4
     900        } else {
     901                ...
     902        }
     903        // implicit break
     904  case 4:
     905
     906
     907
     908
     909\end{cfa}
     910&
     911\begin{cfa}
     912choose ( ... ) {
     913  case 3:
     914        ... Ā®fallthrough common;Ā®
     915  case 4:
     916        ... Ā®fallthrough common;Ā®
     917
     918  Ā®common:Ā® // below fallthrough
     919                          // at case-clause level
     920        ...     // common code for cases 3/4
     921        // implicit break
     922  case 4:
     923
     924
     925\end{cfa}
     926&
     927\begin{cfa}
     928choose ( ... ) {
     929  case 3:
     930        choose ( ... ) {
     931          case 4:
     932                for ( ... ) {
     933                        // multi-level transfer
     934                        ... Ā®fallthru common;Ā®
     935                }
     936                ...
     937        }
    992938        ...
    993   case Ā®2, 4, 6Ā®:
    994         ...
    995 }
     939  Ā®common:Ā® // below fallthrough
     940                          // at case-clause level
     941\end{cfa}
     942\end{tabular}
     943\end{center}
     944The target label must be below the Ā©fallthroughĀ© and may not be nested in a control structure, and
     945the target label must be at the same or higher level as the containing Ā©caseĀ© clause and located at
     946the same level as a Ā©caseĀ© clause; the target label may be case Ā©defaultĀ©, but only associated
     947with the current Ā©switchĀ©/Ā©chooseĀ© statement.
     948
     949\begin{figure}
     950\begin{tabular}{@{}l|l@{}}
     951\multicolumn{1}{c|}{loop control} & \multicolumn{1}{c}{output} \\
     952\hline
     953\begin{cfa}[xleftmargin=0pt]
     954while Ā®()Ā® { sout | "empty"; break; }
     955do { sout | "empty"; break; } while Ā®()Ā®;
     956for Ā®()Ā® { sout | "empty"; break; }
     957for ( Ā®0Ā® ) { sout | "A"; } sout | "zero";
     958for ( Ā®1Ā® ) { sout | "A"; }
     959for ( Ā®10Ā® ) { sout | "A"; }
     960for ( Ā®= 10Ā® ) { sout | "A"; }
     961for ( Ā®1 ~= 10 ~ 2Ā® ) { sout | "B"; }
     962for ( Ā®10 -~= 1 ~ 2Ā® ) { sout | "C"; }
     963for ( Ā®0.5 ~ 5.5Ā® ) { sout | "D"; }
     964for ( Ā®5.5 -~ 0.5Ā® ) { sout | "E"; }
     965for ( Ā®i; 10Ā® ) { sout | i; }
     966for ( Ā®i; = 10Ā® ) { sout | i; }
     967for ( Ā®i; 1 ~= 10 ~ 2Ā® ) { sout | i; }
     968for ( Ā®i; 10 -~= 1 ~ 2Ā® ) { sout | i; }
     969for ( Ā®i; 0.5 ~ 5.5Ā® ) { sout | i; }
     970for ( Ā®i; 5.5 -~ 0.5Ā® ) { sout | i; }
     971for ( Ā®ui; 2u ~= 10u ~ 2uĀ® ) { sout | ui; }
     972for ( Ā®ui; 10u -~= 2u ~ 2uĀ® ) { sout | ui; }
     973enum { N = 10 };
     974for ( Ā®NĀ® ) { sout | "N"; }
     975for ( Ā®i; NĀ® ) { sout | i; }
     976for ( Ā®i; N -~ 0Ā® ) { sout | i; }
     977const int start = 3, comp = 10, inc = 2;
     978for ( Ā®i; start ~ comp ~ inc + 1Ā® ) { sout | i; }
     979for ( i; 1 ~ Ā®@Ā® ) { if ( i > 10 ) break; sout | i; }
     980for ( i; 10 -~ Ā®@Ā® ) { if ( i < 0 ) break; sout | i; }
     981for ( i; 2 ~ Ā®@Ā® ~ 2 ) { if ( i > 10 ) break; sout | i; }
     982for ( i; 2.1 ~ Ā®@Ā® ~ Ā®@Ā® ) { if ( i > 10.5 ) break; sout | i; i += 1.7; }
     983for ( i; 10 -~ Ā®@Ā® ~ 2 ) { if ( i < 0 ) break; sout | i; }
     984for ( i; 12.1 ~ Ā®@Ā® ~ Ā®@Ā® ) { if ( i < 2.5 ) break; sout | i; i -= 1.7; }
     985for ( i; 5 Ā®:Ā® j; -5 ~ @ ) { sout | i | j; }
     986for ( i; 5 Ā®:Ā® j; -5 -~ @ ) { sout | i | j; }
     987for ( i; 5 Ā®:Ā® j; -5 ~ @ ~ 2 ) { sout | i | j; }
     988for ( i; 5 Ā®:Ā® j; -5 -~ @ ~ 2 ) { sout | i | j; }
     989for ( i; 5 Ā®:Ā® j; -5 ~ @ ) { sout | i | j; }
     990for ( i; 5 Ā®:Ā® j; -5 -~ @ ) { sout | i | j; }
     991for ( i; 5 Ā®:Ā® j; -5 ~ @ ~ 2 ) { sout | i | j; }
     992for ( i; 5 Ā®:Ā® j; -5 -~ @ ~ 2 ) { sout | i | j; }
     993for ( i; 5 Ā®:Ā® j; -5 -~ @ ~ 2 Ā®:Ā® k; 1.5 ~ @ ) { sout | i | j | k; }
     994for ( i; 5 Ā®:Ā® j; -5 -~ @ ~ 2 Ā®:Ā® k; 1.5 ~ @ ) { sout | i | j | k; }
     995for ( i; 5 Ā®:Ā® k; 1.5 ~ @ Ā®:Ā® j; -5 -~ @ ~ 2 ) { sout | i | j | k; }
    996996\end{cfa}
    997997&
    998998\begin{cfa}
    999 switch ( i ) {
    1000   case 1: case 3 : case 5:
    1001         ...
    1002   case 2: case 4 : case 6:
    1003         ...
    1004 }
    1005 \end{cfa}
    1006 &
    1007 \begin{cfa}
    1008 
    1009 // odd values
    1010 
    1011 // even values
    1012 
    1013 
     999empty
     1000empty
     1001empty
     1002zero
     1003A
     1004A A A A A A A A A A
     1005A A A A A A A A A A A
     1006B B B B B
     1007C C C C C
     1008D D D D D
     1009E E E E E
     10100 1 2 3 4 5 6 7 8 9
     10110 1 2 3 4 5 6 7 8 9 10
     10121 3 5 7 9
     101310 8 6 4 2
     10140.5 1.5 2.5 3.5 4.5
     10155.5 4.5 3.5 2.5 1.5
     10162 4 6 8 10
     101710 8 6 4 2
     1018
     1019N N N N N N N N N N
     10200 1 2 3 4 5 6 7 8 9
     102110 9 8 7 6 5 4 3 2 1
     1022
     10233 6 9
     10241 2 3 4 5 6 7 8 9 10
     102510 9 8 7 6 5 4 3 2 1 0
     10262 4 6 8 10
     10272.1 3.8 5.5 7.2 8.9
     102810 8 6 4 2 0
     102912.1 10.4 8.7 7. 5.3 3.6
     10300 -5 1 -4 2 -3 3 -2 4 -1
     10310 -5 1 -6 2 -7 3 -8 4 -9
     10320 -5 1 -3 2 -1 3 1 4 3
     10330 -5 1 -7 2 -9 3 -11 4 -13
     10340 -5 1 -4 2 -3 3 -2 4 -1
     10350 -5 1 -6 2 -7 3 -8 4 -9
     10360 -5 1 -3 2 -1 3 1 4 3
     10370 -5 1 -7 2 -9 3 -11 4 -13
     10380 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5
     10390 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5
     10400 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5
    10141041\end{cfa}
    10151042\end{tabular}
    1016 \end{cquote}
    1017 In addition, subranges are allowed to specify case values.\footnote{
    1018 gcc has the same mechanism but awkward syntax, \lstinline@2 ...42@, because a space is required after a number, otherwise the period is a decimal point.}
    1019 \begin{cfa}
    1020 switch ( i ) {
    1021   case Ā®1~5:Ā® §\C{// 1, 2, 3, 4, 5}§
    1022         ...
    1023   case Ā®10~15:Ā® §\C{// 10, 11, 12, 13, 14, 15}§
    1024         ...
    1025 }
    1026 \end{cfa}
    1027 Lists of subranges are also allowed.
    1028 \begin{cfa}
    1029 case Ā®1~5, 12~21, 35~42Ā®:
    1030 \end{cfa}
    1031 
     1043\caption{Loop Control Examples}
     1044\label{f:LoopControlExamples}
     1045\end{figure}
    10321046
    10331047% for ()  => for ( ;; )
     
    10401054
    10411055
     1056\subsection{Loop Control}
     1057
     1058The Ā©forĀ©/Ā©whileĀ©/Ā©do-whileĀ© loop-control allows empty or simplified ranges (see Figure~\ref{f:LoopControlExamples}).
     1059\begin{itemize}
     1060\item
     1061The loop index is polymorphic in the type of the comparison value N (when the start value is implicit) or the start value M.
     1062\item
     1063An empty conditional implies comparison value of Ā©1Ā© (true).
     1064\item
     1065A comparison N is implicit up-to exclusive range [0,N©®)®©.
     1066\item
     1067A comparison Ā©=Ā© N is implicit up-to inclusive range [0,N©®]®©.
     1068\item
     1069The up-to range M Ā©~Ā©\index{~@Ā©~Ā©} N means exclusive range [M,N©®)®©.
     1070\item
     1071The up-to range M Ā©~=Ā©\index{~=@Ā©~=Ā©} N means inclusive range [M,N©®]®©.
     1072\item
     1073The down-to range M Ā©-~Ā©\index{-~@Ā©-~Ā©} N means exclusive range [N,M©®)®©.
     1074\item
     1075The down-to range M Ā©-~=Ā©\index{-~=@Ā©-~=Ā©} N means inclusive range [N,M©®]®©.
     1076\item
     1077Ā©0Ā© is the implicit start value;
     1078\item
     1079Ā©1Ā© is the implicit increment value.
     1080\item
     1081The up-to range uses operator Ā©+=Ā© for increment;
     1082\item
     1083The down-to range uses operator Ā©-=Ā© for decrement.
     1084\item
     1085Ā©@Ā© means put nothing in this field.
     1086\item
     1087Ā©:Ā© means start another index.
     1088\end{itemize}
     1089
     1090
    10421091%\subsection{\texorpdfstring{Labelled \protect\lstinline@continue@ / \protect\lstinline@break@}{Labelled continue / break}}
    10431092\subsection{\texorpdfstring{Labelled \LstKeywordStyle{continue} / \LstKeywordStyle{break} Statement}{Labelled continue / break Statement}}
     
    10491098for ©break©, the target label can also be associated with a ©switch©, ©if© or compound (©{}©) statement.
    10501099\VRef[Figure]{f:MultiLevelExit} shows ©continue© and ©break© indicating the specific control structure, and the corresponding C program using only ©goto© and labels.
    1051 The innermost loop has 7 exit points, which cause continuation or termination of one or more of the 7 \Index{nested control-structure}s.
     1100The innermost loop has 8 exit points, which cause continuation or termination of one or more of the 7 \Index{nested control-structure}s.
    10521101
    10531102\begin{figure}
    1054 \begin{tabular}{@{\hspace{\parindentlnth}}l@{\hspace{\parindentlnth}}l@{\hspace{\parindentlnth}}l@{}}
    1055 \multicolumn{1}{@{\hspace{\parindentlnth}}c@{\hspace{\parindentlnth}}}{\textbf{\CFA}}   & \multicolumn{1}{@{\hspace{\parindentlnth}}c}{\textbf{C}}      \\
    1056 \begin{cfa}
    1057 Ā®LC:Ā® {
    1058         ... §declarations§ ...
    1059         Ā®LS:Ā® switch ( ... ) {
    1060           case 3:
    1061                 Ā®LIF:Ā® if ( ... ) {
    1062                         Ā®LF:Ā® for ( ... ) {
    1063                                 Ā®LW:Ā® while ( ... ) {
    1064                                         ... break Ā®LCĀ®; ...
    1065                                         ... break Ā®LSĀ®; ...
    1066                                         ... break Ā®LIFĀ®; ...
    1067                                         ... continue Ā®LF;Ā® ...
    1068                                         ... break Ā®LFĀ®; ...
    1069                                         ... continue Ā®LWĀ®; ...
    1070                                         ... break Ā®LWĀ®; ...
    1071                                 } // while
    1072                         } // for
    1073                 } else {
    1074                         ... break Ā®LIFĀ®; ...
    1075                 } // if
    1076         } // switch
     1103\centering
     1104\begin{lrbox}{\myboxA}
     1105\begin{cfa}[tabsize=3]
     1106Ā®Compound:Ā® {
     1107        Ā®Try:Ā® try {
     1108                Ā®For:Ā® for ( ... ) {
     1109                        Ā®While:Ā® while ( ... ) {
     1110                                Ā®Do:Ā® do {
     1111                                        Ā®If:Ā® if ( ... ) {
     1112                                                Ā®Switch:Ā® switch ( ... ) {
     1113                                                        case 3:
     1114                                                                Ā®break CompoundĀ®;
     1115                                                                Ā®break TryĀ®;
     1116                                                                Ā®break ForĀ®;      /* or */  Ā®continue ForĀ®;
     1117                                                                Ā®break WhileĀ®;  /* or */  Ā®continue WhileĀ®;
     1118                                                                Ā®break DoĀ®;      /* or */  Ā®continue DoĀ®;
     1119                                                                Ā®break IfĀ®;
     1120                                                                Ā®break SwitchĀ®;
     1121                                                        } // switch
     1122                                                } else {
     1123                                                        ... Ā®break IfĀ®; ...     // terminate if
     1124                                                } // if
     1125                                } while ( ... ); // do
     1126                        } // while
     1127                } // for
     1128        } Ā®finallyĀ® { // always executed
     1129        } // try
    10771130} // compound
    10781131\end{cfa}
    1079 &
    1080 \begin{cfa}
     1132\end{lrbox}
     1133
     1134\begin{lrbox}{\myboxB}
     1135\begin{cfa}[tabsize=3]
    10811136{
    1082         ... §declarations§ ...
    1083         switch ( ... ) {
    1084           case 3:
    1085                 if ( ... ) {
    1086                         for ( ... ) {
    1087                                 while ( ... ) {
    1088                                         ... goto Ā®LCĀ®; ...
    1089                                         ... goto Ā®LSĀ®; ...
    1090                                         ... goto Ā®LIFĀ®; ...
    1091                                         ... goto Ā®LFCĀ®; ...
    1092                                         ... goto Ā®LFBĀ®; ...
    1093                                         ... goto Ā®LWCĀ®; ...
    1094                                         ... goto Ā®LWBĀ®; ...
    1095                                   Ā®LWCĀ®: ; } Ā®LWB:Ā® ;
    1096                           Ā®LFC:Ā® ; } Ā®LFB:Ā® ;
    1097                 } else {
    1098                         ... goto Ā®LIFĀ®; ...
    1099                 } Ā®L3:Ā® ;
    1100         } Ā®LS:Ā® ;
    1101 } Ā®LC:Ā® ;
    1102 \end{cfa}
    1103 &
    1104 \begin{cfa}
    1105 
    1106 
    1107 
    1108 
    1109 
    1110 
    1111 
    1112 // terminate compound
    1113 // terminate switch
    1114 // terminate if
    1115 // continue loop
    1116 // terminate loop
    1117 // continue loop
    1118 // terminate loop
    1119 
    1120 
    1121 
    1122 // terminate if
    1123 
    1124 
    1125 
    1126 \end{cfa}
    1127 \end{tabular}
     1137
     1138                Ā®ForC:Ā® for ( ... ) {
     1139                        Ā®WhileC:Ā® while ( ... ) {
     1140                                Ā®DoC:Ā® do {
     1141                                        if ( ... ) {
     1142                                                switch ( ... ) {
     1143                                                        case 3:
     1144                                                                Ā®goto CompoundĀ®;
     1145                                                                Ā®goto TryĀ®;
     1146                                                                Ā®goto ForBĀ®;      /* or */  Ā®goto ForCĀ®;
     1147                                                                Ā®goto WhileBĀ®;  /* or */  Ā®goto WhileCĀ®;
     1148                                                                Ā®goto DoBĀ®;      /* or */  Ā®goto DoCĀ®;
     1149                                                                Ā®goto IfĀ®;
     1150                                                                Ā®goto SwitchĀ®;
     1151                                                        } Ā®Switch:Ā® ;
     1152                                                } else {
     1153                                                        ... Ā®goto IfĀ®; ...      // terminate if
     1154                                                } Ā®If:Ā®;
     1155                                } while ( ... ); Ā®DoB:Ā® ;
     1156                        } Ā®WhileB:Ā® ;
     1157                } Ā®ForB:Ā® ;
     1158
     1159
     1160} Ā®Compound:Ā® ;
     1161\end{cfa}
     1162\end{lrbox}
     1163
     1164\subfloat[\CFA]{\label{f:CFibonacci}\usebox\myboxA}
     1165\hspace{2pt}
     1166\vrule
     1167\hspace{2pt}
     1168\subfloat[C]{\label{f:CFAFibonacciGen}\usebox\myboxB}
    11281169\caption{Multi-level Exit}
    11291170\label{f:MultiLevelExit}
     
    13801421try {
    13811422        f(...);
    1382 } catch( E e ; §boolean-predicate§ ) {          §\C[8cm]{// termination handler}§
     1423} catch( E e ; §boolean-predicate§ ) {          §\C{// termination handler}§
    13831424        // recover and continue
    1384 } catchResume( E e ; §boolean-predicate§ ) { §\C{// resumption handler}\CRT§
     1425} catchResume( E e ; §boolean-predicate§ ) { §\C{// resumption handler}§
    13851426        // repair and return
    13861427} finally {
     
    34453486For implicit formatted input, the common case is reading a sequence of values separated by whitespace, where the type of an input constant must match with the type of the input variable.
    34463487\begin{cquote}
    3447 \begin{lrbox}{\LstBox}
     3488\begin{lrbox}{\myboxA}
    34483489\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    34493490int x;   double y   char z;
     
    34513492\end{lrbox}
    34523493\begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{3em}}l@{}}
    3453 \multicolumn{1}{@{}l@{}}{\usebox\LstBox} \\
     3494\multicolumn{1}{@{}l@{}}{\usebox\myboxA} \\
    34543495\multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CFA}}        & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CC}}       & \multicolumn{1}{c}{\textbf{Python}}   \\
    34553496\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     
    65476588hence, names in these include files are not mangled\index{mangling!name} (see~\VRef{s:Interoperability}).
    65486589All other C header files must be explicitly wrapped in ©extern "C"© to prevent name mangling.
    6549 For \Index*[C++]{\CC{}}, the name-mangling issue is often handled internally in many C header-files through checks for preprocessor variable Ā©__cplusplusĀ©, which adds appropriate Ā©extern "C"Ā© qualifiers.
     6590This approach is different from \Index*[C++]{\CC{}} where the name-mangling issue is handled internally in C header-files through checks for preprocessor variable Ā©__cplusplusĀ©, which adds appropriate Ā©extern "C"Ā© qualifiers.
    65506591
    65516592
     
    65616602The storage-management routines extend their C equivalents by overloading, alternate names, providing shallow type-safety, and removing the need to specify the allocation size for non-array types.
    65626603
    6563 Storage management provides the following capabilities:
     6604C storage management provides the following capabilities:
    65646605\begin{description}
    6565 \item[fill]
    6566 after allocation the storage is filled with a specified character.
     6606\item[filled]
     6607after allocation with a specified character or value.
    65676608\item[resize]
    6568 an existing allocation is decreased or increased in size.
    6569 In either case, new storage may or may not be allocated and, if there is a new allocation, as much data from the existing allocation is copied.
     6609an existing allocation to decreased or increased its size.
     6610In either case, new storage may or may not be allocated and, if there is a new allocation, as much data from the existing allocation is copied into the new allocation.
    65706611For an increase in storage size, new storage after the copied data may be filled.
    6571 \item[alignment]
    6572 an allocation starts on a specified memory boundary, \eg, an address multiple of 64 or 128 for cache-line purposes.
     6612\item[align]
     6613an allocation on a specified memory boundary, \eg, an address multiple of 64 or 128 for cache-line purposes.
    65736614\item[array]
    65746615the allocation size is scaled to the specified number of array elements.
    65756616An array may be filled, resized, or aligned.
    65766617\end{description}
    6577 The table shows allocation routines supporting different combinations of storage-management capabilities:
    6578 \begin{center}
    6579 \begin{tabular}{@{}r|r|l|l|l|l@{}}
     6618\VRef[Table]{t:AllocationVersusCapabilities} shows allocation routines supporting different combinations of storage-management capabilities.
     6619\begin{table}
     6620\centering
     6621\begin{minipage}{0.75\textwidth}
     6622\begin{tabular}{@{}r|l|l|l|l|l@{}}
    65806623\multicolumn{1}{c}{}&           & \multicolumn{1}{c|}{fill}     & resize        & alignment     & array \\
    65816624\hline
    65826625C               & Ā©mallocĀ©                      & no                    & no            & no            & no    \\
    65836626                & Ā©callocĀ©                      & yes (0 only)  & no            & no            & yes   \\
    6584                 & Ā©reallocĀ©                     & no/copy               & yes           & no            & no    \\
     6627                & Ā©reallocĀ©                     & copy                  & yes           & no            & no    \\
    65856628                & Ā©memalignĀ©            & no                    & no            & yes           & no    \\
     6629                & Ā©aligned_allocĀ©\footnote{Same as Ā©memalignĀ© but size is an integral multiple of alignment, which is universally ignored.}
     6630                                                        & no                    & no            & yes           & no    \\
    65866631                & Ā©posix_memalignĀ©      & no                    & no            & yes           & no    \\
     6632                & Ā©vallocĀ©                      & no                    & no            & yes (page size)& no   \\
     6633                & Ā©pvallocĀ©\footnote{Same as Ā©vallocĀ© but rounds size to multiple of page size.}
     6634                                                        & no                    & no            & yes (page size)& no   \\
    65876635\hline
    6588 C11             & Ā©aligned_allocĀ©       & no                    & no            & yes           & no    \\
    6589 \hline
    6590 \CFA    & Ā©allocĀ©                       & no/copy/yes   & no/yes        & no            & yes   \\
    6591                 & Ā©align_allocĀ©         & no/yes                & no            & yes           & yes   \\
     6636\CFA    & Ā©cmemalignĀ©           & yes (0 only)  & no            & yes           & yes   \\
     6637                & Ā©reallocĀ©                     & copy                  & yes           & yes           & no    \\
     6638                & Ā©allocĀ©                       & no                    & yes           & no            & yes   \\
     6639                & Ā©alloc_setĀ©           & yes                   & yes           & no            & yes   \\
     6640                & Ā©alloc_alignĀ©         & no                    & yes           & yes           & yes   \\
     6641                & Ā©alloc_align_setĀ©     & yes                   & yes           & yes           & yes   \\
    65926642\end{tabular}
    6593 \end{center}
    6594 It is impossible to resize with alignment because the underlying Ā©reallocĀ© allocates storage if more space is needed, and it does not honour alignment from the original allocation.
     6643\end{minipage}
     6644\caption{Allocation Routines versus Storage-Management Capabilities}
     6645\label{t:AllocationVersusCapabilities}
     6646\end{table}
     6647
     6648\CFA memory management extends the type safety of all allocations by using the type of the left-hand-side type to determine the allocation size and return a matching type for the new storage.
     6649Type-safe allocation is provided for all C allocation routines and new \CFA allocation routines, \eg in
     6650\begin{cfa}
     6651int * ip = (int *)malloc( sizeof(int) );                §\C{// C}§
     6652int * ip = malloc();                                                    §\C{// \CFA type-safe version of C malloc}§
     6653int * ip = alloc();                                                             Ā§\C{// \CFA type-safe uniform alloc}§
     6654\end{cfa}
     6655the latter two allocations determine the allocation size from the type of Ā©pĀ© (Ā©intĀ©) and cast the pointer to the allocated storage to Ā©int *Ā©.
     6656
     6657\CFA memory management extends allocation safety by implicitly honouring all alignment requirements, \eg in
     6658\begin{cfa}
     6659struct S { int i; } __attribute__(( aligned( 128 ) )); // cache-line alignment
     6660S * sp = malloc();                                                              §\C{// honour type alignment}§
     6661\end{cfa}
     6662the storage allocation is implicitly aligned to 128 rather than the default 16.
     6663The alignment check is performed at compile time so there is no runtime cost.
     6664
     6665\CFA memory management extends the resize capability with the notion of \newterm{sticky properties}.
     6666Hence, initial allocation capabilities are remembered and maintained when resize requires copying.
     6667For example, an initial alignment and fill capability are preserved during a resize copy so the copy has the same alignment and extended storage is filled.
     6668Without sticky properties it is dangerous to use Ā©reallocĀ©, resulting in an idiom of manually performing the reallocation to maintain correctness.
     6669\begin{cfa}
     6670
     6671\end{cfa}
     6672
     6673\CFA memory management extends allocation to support constructors for initialization of allocated storage, \eg in
     6674\begin{cfa}
     6675struct S { int i; };                                                    §\C{// cache-line aglinment}§
     6676void ?{}( S & s, int i ) { s.i = i; }
     6677// assume ?|? operator for printing an S
     6678
     6679S & sp = *Ā®newĀ®( 3 );                                                   Ā§\C{// call constructor after allocation}§
     6680sout | sp.i;
     6681Ā®deleteĀ®( &sp );
     6682
     6683S * spa = Ā®anewĀ®( 10, 5 );                                              §\C{// allocate array and initialize each array element}§
     6684for ( i; 10 ) sout | spa[i] | nonl;
     6685sout | nl;
     6686Ā®adeleteĀ®( 10, spa );
     6687\end{cfa}
     6688Allocation routines Ā©newĀ©/Ā©anewĀ© allocate a variable/array and initialize storage using the allocated type's constructor.
     6689Note, the matching deallocation routines Ā©deleteĀ©/Ā©adeleteĀ©.
    65956690
    65966691\leavevmode
    65976692\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    6598 // C unsafe allocation
    65996693extern "C" {
    6600 void * malloc( size_t size );§\indexc{memset}§
    6601 void * calloc( size_t dim, size_t size );§\indexc{calloc}§
    6602 void * realloc( void * ptr, size_t size );§\indexc{realloc}§
    6603 void * memalign( size_t align, size_t size );§\indexc{memalign}§
    6604 int posix_memalign( void ** ptr, size_t align, size_t size );§\indexc{posix_memalign}§
    6605 
    6606 // C unsafe initialization/copy
    6607 void * memset( void * dest, int c, size_t size );
    6608 void * memcpy( void * dest, const void * src, size_t size );
    6609 }
     6694        // C unsafe allocation
     6695        void * malloc( size_t size );§\indexc{malloc}§
     6696        void * calloc( size_t dim, size_t size );§\indexc{calloc}§
     6697        void * realloc( void * ptr, size_t size );§\indexc{realloc}§
     6698        void * memalign( size_t align, size_t size );§\indexc{memalign}§
     6699        void * aligned_alloc( size_t align, size_t size );§\indexc{aligned_alloc}§
     6700        int posix_memalign( void ** ptr, size_t align, size_t size );§\indexc{posix_memalign}§
     6701        void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize );§\indexc{cmemalign}§ // CFA
     6702
     6703        // C unsafe initialization/copy
     6704        void * memset( void * dest, int c, size_t size );§\indexc{memset}§
     6705        void * memcpy( void * dest, const void * src, size_t size );§\indexc{memcpy}§
     6706}
     6707
     6708void * realloc( void * oaddr, size_t nalign, size_t size ); // CFA heap
    66106709
    66116710forall( dtype T | sized(T) ) {
    6612 // §\CFA§ safe equivalents, i.e., implicit size specification
     6711        // §\CFA§ safe equivalents, i.e., implicit size specification
    66136712        T * malloc( void );
    66146713        T * calloc( size_t dim );
    66156714        T * realloc( T * ptr, size_t size );
    66166715        T * memalign( size_t align );
     6716        T * cmemalign( size_t align, size_t dim  );
    66176717        T * aligned_alloc( size_t align );
    66186718        int posix_memalign( T ** ptr, size_t align );
    66196719
    6620 // §\CFA§ safe general allocation, fill, resize, array
    6621         T * alloc( void );§\indexc{alloc}§
    6622         T * alloc( char fill );
    6623         T * alloc( size_t dim );
    6624         T * alloc( size_t dim, char fill );
    6625         T * alloc( T ptr[], size_t dim );
    6626         T * alloc( T ptr[], size_t dim, char fill );
    6627 
    6628 // §\CFA§ safe general allocation, align, fill, array
    6629         T * align_alloc( size_t align );
    6630         T * align_alloc( size_t align, char fill );
    6631         T * align_alloc( size_t align, size_t dim );
    6632         T * align_alloc( size_t align, size_t dim, char fill );
    6633 
    6634 // §\CFA§ safe initialization/copy, i.e., implicit size specification
    6635         T * memset( T * dest, char c );§\indexc{memset}§
     6720        // §\CFA§ safe general allocation, fill, resize, alignment, array
     6721        T * alloc( void );§\indexc{alloc}§                                      §\C[3.5in]{// variable, T size}§
     6722        T * alloc( size_t dim );                                                        §\C{// array[dim], T size elements}§
     6723        T * alloc( T ptr[], size_t dim );                                       Ā§\C{// realloc array[dim], T size elements}§
     6724
     6725        T * alloc_set( char fill );§\indexc{alloc_set}§         Ā§\C{// variable, T size, fill bytes with value}§
     6726        T * alloc_set( T fill );                                                        §\C{// variable, T size, fill with value}§
     6727        T * alloc_set( size_t dim, char fill );                         Ā§\C{// array[dim], T size elements, fill bytes with value}§
     6728        T * alloc_set( size_t dim, T fill );                            §\C{// array[dim], T size elements, fill elements with value}§
     6729        T * alloc_set( size_t dim, const T fill[] );            §\C{// array[dim], T size elements, fill elements with array}§
     6730        T * alloc_set( T ptr[], size_t dim, char fill );        §\C{// realloc array[dim], T size elements, fill bytes with value}§
     6731
     6732        T * alloc_align( size_t align );                                        §\C{// aligned variable, T size}§
     6733        T * alloc_align( size_t align, size_t dim );            §\C{// aligned array[dim], T size elements}§
     6734        T * alloc_align( T ptr[], size_t align );                       Ā§\C{// realloc new aligned array}§
     6735        T * alloc_align( T ptr[], size_t align, size_t dim ); §\C{// realloc new aligned array[dim]}§
     6736
     6737        T * alloc_align_set( size_t align, char fill );         Ā§\C{// aligned variable, T size, fill bytes with value}§
     6738        T * alloc_align_set( size_t align, T fill );            §\C{// aligned variable, T size, fill with value}§
     6739        T * alloc_align_set( size_t align, size_t dim, char fill ); §\C{// aligned array[dim], T size elements, fill bytes with value}§
     6740        T * alloc_align_set( size_t align, size_t dim, T fill ); §\C{// aligned array[dim], T size elements, fill elements with value}§
     6741        T * alloc_align_set( size_t align, size_t dim, const T fill[] ); §\C{// aligned array[dim], T size elements, fill elements with array}§
     6742        T * alloc_align_set( T ptr[], size_t align, size_t dim, char fill ); §\C{// realloc new aligned array[dim], fill new bytes with value}§
     6743
     6744        // §\CFA§ safe initialization/copy, i.e., implicit size specification
     6745        T * memset( T * dest, char fill );§\indexc{memset}§
    66366746        T * memcpy( T * dest, const T * src );§\indexc{memcpy}§
    66376747
    6638 // §\CFA§ safe initialization/copy array
    6639         T * amemset( T dest[], char c, size_t dim );
     6748        // §\CFA§ safe initialization/copy, i.e., implicit size specification, array types
     6749        T * amemset( T dest[], char fill, size_t dim );
    66406750        T * amemcpy( T dest[], const T src[], size_t dim );
    66416751}
    66426752
    6643 // §\CFA§ allocation/deallocation and constructor/destructor
    6644 forall( dtype T | sized(T), ttype Params | { void ?{}( T *, Params ); } ) T * new( Params p );§\indexc{new}§
    6645 forall( dtype T | { void ^?{}( T * ); } ) void delete( T * ptr );§\indexc{delete}§
    6646 forall( dtype T, ttype Params | { void ^?{}( T * ); void delete( Params ); } )
     6753// §\CFA§ allocation/deallocation and constructor/destructor, non-array types
     6754forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p );§\indexc{new}§
     6755forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr );§\indexc{delete}§
     6756forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } )
    66476757  void delete( T * ptr, Params rest );
    66486758
    6649 // §\CFA§ allocation/deallocation and constructor/destructor, array
    6650 forall( dtype T | sized(T), ttype Params | { void ?{}( T *, Params ); } ) T * anew( size_t dim, Params p );§\indexc{anew}§
    6651 forall( dtype T | sized(T) | { void ^?{}( T * ); } ) void adelete( size_t dim, T arr[] );§\indexc{adelete}§
    6652 forall( dtype T | sized(T) | { void ^?{}( T * ); }, ttype Params | { void adelete( Params ); } )
     6759// §\CFA§ allocation/deallocation and constructor/destructor, array types
     6760forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size_t dim, Params p );§\indexc{anew}§
     6761forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( size_t dim, T arr[] );§\indexc{adelete}§
     6762forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params ); } )
    66536763  void adelete( size_t dim, T arr[], Params rest );
    66546764\end{cfa}
  • driver/Makefile.am

    r3c64c668 r58fe85a  
    2828        @test -z "$(CFA_BINDIR)" || $(MKDIR_P) "$(CFA_BINDIR)"
    2929        @echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) cfa '$(CFA_BINDIR)/$(CFA_NAME)'"; \
     30        chmod u+w $(CFA_BINDIR);\
    3031        $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) cfa $(CFA_BINDIR)/$(CFA_NAME) || exit $$?
    3132
  • driver/cc1.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Fri Aug 26 14:23:51 2005
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 20 08:14:33 2019
    13 // Update Count     : 385
     12// Last Modified On : Tue Nov 17 14:27:08 2020
     13// Update Count     : 414
    1414//
    1515
     
    2424#include <unistd.h>                                                                             // execvp, fork, unlink
    2525#include <sys/wait.h>                                                                   // wait
    26 #include <fcntl.h>
     26#include <fcntl.h>                                                                              // creat
    2727
    2828
     
    3838static string o_file;
    3939static string bprefix;
     40static string lang;                                                                             // -x flag
    4041
    4142
     
    5859
    5960
    60 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" );               // "N__=" suffix
     61static string __CFA_FLAGPREFIX__( "__CFA_FLAG" );               // "__CFA_FLAG__=" suffix
    6162
    6263static void checkEnv1( const char * args[], int & nargs ) { // stage 1
     
    7374                        if ( prefix( val, "-compiler=" ) ) {
    7475                                compiler_path = val.substr( 10 );
     76                        } else if ( prefix( val, "-x=" ) ) {
     77                                lang = val.substr( 3 );
    7578                        } // if
    7679                } // if
     
    9497                        } else if ( val == "-CFA" ) {
    9598                                CFA_flag = true;
    96                         } else if ( val == "-save-temps" ) {
     99                        } else if ( val == "-save-temps" || val == "--save-temps" ) {
    97100                                save_temps = true;
    98101                        } else if ( prefix( val, "-o=" ) ) {            // output file for -CFA
     
    100103                        } else if ( prefix( val, "-B=" ) ) {            // location of cfa-cpp
    101104                                bprefix = val.substr( 3 );
     105                        } else if ( prefix( val, "-x=" ) ) {            // ignore
    102106                        } else {                                                                        // normal flag for cfa-cpp
    103107                                args[nargs++] = ( *new string( arg.substr( arg.find_first_of( "=" ) + 1 ) ) ).c_str();
     
    107111} // checkEnv2
    108112
    109 
    110 static char tmpname[] = P_tmpdir "/CFAXXXXXX.ifa";
     113#define CFA_SUFFIX ".ifa"
     114
     115static char tmpname[] = P_tmpdir "/CFAXXXXXX" CFA_SUFFIX;
    111116static int tmpfilefd = -1;
    112117static bool startrm = false;
     
    166171                        if ( arg == "-quiet" ) {
    167172                        } else if ( arg == "-imultilib" || arg == "-imultiarch" ) {
    168                                 i += 1;                                                                 // and the argument
     173                                i += 1;                                                                 // and argument
    169174                        } else if ( prefix( arg, "-A" ) ) {
    170175                        } else if ( prefix( arg, "-D__GNU" ) ) {
     
    173178                                //********
    174179                        } else if ( arg == "-D" && prefix( argv[i + 1], "__GNU" ) ) {
    175                                 i += 1;                                                                 // and the argument
     180                                i += 1;                                                                 // and argument
    176181
    177182                                // strip flags controlling cpp step
     
    180185                                cpp_flag = true;
    181186                        } else if ( arg == "-D" && string( argv[i + 1] ) == "__CPP__" ) {
    182                                 i += 1;                                                                 // and the argument
     187                                i += 1;                                                                 // and argument
    183188                                cpp_flag = true;
    184189
     
    190195                                cpp_out = argv[i];
    191196                        } else {
    192                                 args[nargs++] = argv[i];                                // pass the flag along
     197                                args[nargs++] = argv[i];                                // pass flag along
    193198                                // CPP flags with an argument
    194199                                if ( arg == "-D" || arg == "-U" || arg == "-I" || arg == "-MF" || arg == "-MT" || arg == "-MQ" ||
     
    196201                                         arg == "-iwithprefix" || arg == "-iwithprefixbefore" || arg == "-isystem" || arg == "-isysroot" ) {
    197202                                        i += 1;
    198                                         args[nargs++] = argv[i];                        // pass the argument along
     203                                        args[nargs++] = argv[i];                        // pass argument along
    199204                                        #ifdef __DEBUG_H__
    200205                                        cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
    201206                                        #endif // __DEBUG_H__
    202207                                } else if ( arg == "-MD" || arg == "-MMD" ) {
     208                                        // gcc frontend generates the dependency file-name after the -MD/-MMD flag, but it is necessary to
     209                                        // prefix that file name with -MF.
    203210                                        args[nargs++] = "-MF";                          // insert before file
    204211                                        i += 1;
    205                                         args[nargs++] = argv[i];                        // pass the argument along
     212                                        args[nargs++] = argv[i];                        // pass argument along
    206213                                        #ifdef __DEBUG_H__
    207214                                        cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
     
    247254
    248255                args[0] = compiler_path.c_str();
    249                 suffix( cpp_in, args, nargs );                                  // check suffix
     256                if ( lang.size() == 0 ) {
     257                        suffix( cpp_in, args, nargs );                          // check suffix
     258                } else {
     259                        args[nargs++] = "-x";
     260                        args[nargs++] = ( *new string( lang.c_str() ) ).c_str();
     261                } // if
    250262                args[nargs++] = cpp_in;
    251263                if ( o_flag ) {                                                                 // location for output
     
    270282        // Run the C preprocessor and save the output in the given file.
    271283
    272         if ( fork() == 0 ) {                                                             // child process ?
     284        if ( fork() == 0 ) {                                                            // child process ?
    273285                // -o xxx.ii cannot be used to write the output file from cpp because no output file is created if cpp detects
    274286                // an error (e.g., cannot find include file). Whereas, output is always generated, even when there is an error,
     
    280292
    281293                args[0] = compiler_path.c_str();
    282                 suffix( cpp_in, args, nargs );                                  // check suffix
     294                if ( lang.size() == 0 ) {
     295                        suffix( cpp_in, args, nargs );                          // check suffix
     296                } else {
     297                        args[nargs++] = "-x";
     298                        args[nargs++] = ( *new string( lang.c_str() ) ).c_str();
     299                } // if
    283300                args[nargs++] = cpp_in;                                                 // input to cpp
    284301                args[nargs] = nullptr;                                                  // terminate argument list
     
    305322
    306323        if ( WIFSIGNALED(code) ) {                                                      // child failed ?
     324                rmtmpfile();                                                                    // remove tmpname
    307325                cerr << "CC1 Translator error: stage 1, child failed " << WTERMSIG(code) << endl;
    308326                exit( EXIT_FAILURE );
    309327        } // if
    310328
    311         exit( WEXITSTATUS(code) );                                                      // bad cpp result stops top-level gcc
     329        exit( WEXITSTATUS( code ) );                                            // bad cpp result stops top-level gcc
    312330} // Stage1
    313331
     
    357375                        } else if ( arg == "-fno-diagnostics-color" ) {
    358376                                color_arg = Color_Auto;
    359                         }
     377                        } // if
    360378
    361379                        if ( arg == "-quiet" || arg == "-version" || arg == "-fpreprocessed" ||
    362                                 // Currently CFA does not suppose precompiled .h files.
    363                                 prefix( arg, "--output-pch" ) ) {
     380                                 // Currently CFA does not suppose precompiled .h files.
     381                                 prefix( arg, "--output-pch" ) ) {
    364382
    365383                                // strip inappropriate flags with an argument
     
    374392
    375393                        } else {
    376                                 args[nargs++] = argv[i];                                // pass the flag along
     394                                args[nargs++] = argv[i];                                // pass flag along
    377395                                if ( arg == "-o" ) {
    378396                                        i += 1;
    379397                                        cpp_out = argv[i];
    380                                         args[nargs++] = argv[i];                        // pass the argument along
     398                                        args[nargs++] = argv[i];                        // pass argument along
    381399                                        #ifdef __DEBUG_H__
    382400                                        cerr << "arg:\"" << argv[i] << "\"" << endl;
     
    425443                        } // if
    426444
    427                         cfa_cpp_out = cfa_cpp_out.substr( 0, dot ) + ".ifa";
     445                        cfa_cpp_out = cfa_cpp_out.substr( 0, dot ) + CFA_SUFFIX;
    428446                        if ( creat( cfa_cpp_out.c_str(), 0666 ) == -1 ) {
    429447                                perror( "CC1 Translator error: stage 2, creat" );
     
    446464        // output.  Otherwise, run the cfa-cpp preprocessor on the temporary file and save the result into the output file.
    447465
    448         if ( fork() == 0 ) {                                                            // child runs CFA
     466        if ( fork() == 0 ) {                                                            // child runs CFA preprocessor
    449467                cargs[0] = ( *new string( bprefix + "cfa-cpp" ) ).c_str();
    450468                cargs[ncargs++] = cpp_in;
     
    504522        #endif // __DEBUG_H__
    505523
    506         if ( fork() == 0 ) {                                                            // child runs CFA
     524        if ( fork() == 0 ) {                                                            // child runs gcc
    507525                args[0] = compiler_path.c_str();
    508526                args[nargs++] = "-S";                                                   // only compile and put assembler output in specified file
  • driver/cfa.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Aug 20 13:44:49 2002
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Jan 31 16:48:03 2020
    13 // Update Count     : 421
     12// Last Modified On : Tue Nov 17 14:27:28 2020
     13// Update Count     : 440
    1414//
    1515
    1616#include <iostream>
    17 #include <cstdio>      // perror
    18 #include <cstdlib>     // putenv, exit
    19 #include <climits>     // PATH_MAX
    20 #include <unistd.h>    // execvp
    21 #include <string>      // STL version
    22 #include <string.h>    // strcmp
    23 #include <algorithm>   // find
    24 
     17#include <cstdio>                                                                               // perror
     18#include <cstdlib>                                                                              // putenv, exit
     19#include <climits>                                                                              // PATH_MAX
     20#include <string>                                                                               // STL version
     21#include <algorithm>                                                                    // find
     22
     23#include <unistd.h>                                                                             // execvp
    2524#include <sys/types.h>
    2625#include <sys/stat.h>
     
    3433using std::to_string;
    3534
    36 // #define __DEBUG_H__
    37 
    38 // "N__=" suffix
    39 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" );
    40 
    41 void Putenv( char * argv[], string arg ) {
     35//#define __DEBUG_H__
     36
     37#define xstr(s) str(s)
     38#define str(s) #s
     39
     40static string __CFA_FLAGPREFIX__( "__CFA_FLAG" );               // "__CFA_FLAG__=" suffix
     41
     42static void Putenv( char * argv[], string arg ) {
    4243        // environment variables must have unique names
    4344        static int flags = 0;
     
    4950} // Putenv
    5051
    51 // check if string has prefix
    52 bool prefix( const string & arg, const string & pre ) {
     52static bool prefix( const string & arg, const string & pre ) { // check if string has prefix
    5353        return arg.substr( 0, pre.size() ) == pre;
    5454} // prefix
    5555
    56 inline bool ends_with(const string & str, const string & sfix) {
     56static inline bool ends_with(const string & str, const string & sfix) {
    5757        if (sfix.size() > str.size()) return false;
    5858        return std::equal(str.rbegin(), str.rbegin() + sfix.size(), sfix.rbegin(), sfix.rend());
     
    6060
    6161// check if string has suffix
    62 bool suffix( const string & arg ) {
     62static bool suffix( const string & arg ) {
    6363        enum { NumSuffixes = 3 };
    6464        static const string suffixes[NumSuffixes] = { "cfa", "hfa", "ifa" };
     
    7070} // suffix
    7171
    72 
    7372static inline bool dirExists( const string & path ) {   // check if directory exists
    7473    struct stat info;
     
    7978static inline string dir(const string & path) {
    8079        return path.substr(0, path.find_last_of('/'));
    81 }
     80} // dir
    8281
    8382// Different path modes
     
    118117}
    119118
    120 
    121 #define xstr(s) str(s)
    122 #define str(s) #s
    123119
    124120int main( int argc, char * argv[] ) {
     
    158154        PathMode path = FromProc();
    159155
    160         const char *args[argc + 100];                                           // cfa command line values, plus some space for additional flags
     156        const char * args[argc + 100];                                          // cfa command line values, plus some space for additional flags
    161157        int sargs = 1;                                                                          // starting location for arguments in args list
    162158        int nargs = sargs;                                                                      // number of arguments in args list; 0 => command name
    163159
    164         const char *libs[argc + 20];                                            // non-user libraries must come separately, plus some added libraries and flags
     160        const char * libs[argc + 20];                                           // non-user libraries must come separately, plus some added libraries and flags
    165161        int nlibs = 0;
    166162
     
    180176
    181177                        if ( arg == "-Xlinker" || arg == "-o" ) {
    182                                 args[nargs++] = argv[i];                                // pass argument along
     178                                args[nargs++] = argv[i];                                // pass flag along
    183179                                i += 1;
    184180                                if ( i == argc ) continue;                              // next argument available ?
    185181                                args[nargs++] = argv[i];                                // pass argument along
    186182                                if ( arg == "-o" ) o_file = i;                  // remember file
    187                         } else if ( arg == "-XCFA" ) {                          // CFA pass through
    188                                 i += 1;
    189                                 if ( i == argc ) continue;                              // next argument available ?
    190                                 Putenv( argv, argv[i] );
    191183
    192184                                // CFA specific arguments
    193185
     186                        } else if ( strncmp(arg.c_str(), "-XCFA", 5) == 0 ) { // CFA pass through
     187                                if ( arg.size() == 5 ) {
     188                                        i += 1;
     189                                        if ( i == argc ) continue;                      // next argument available ?
     190                                        Putenv( argv, argv[i] );
     191                                } else if ( arg[5] == ',' ) {                   // CFA specific arguments
     192                                        Putenv( argv, argv[i] + 6 );
     193                                } else {                                                                // CFA specific arguments
     194                                        args[nargs++] = argv[i];
     195                                } // if
    194196                        } else if ( arg == "-CFA" ) {
    195197                                CFA_flag = true;                                                // strip the -CFA flag
     
    200202                        } else if ( arg == "-nodebug" ) {
    201203                                debug = false;                                                  // strip the nodebug flag
    202                         } else if ( arg == "-nolib" ) {
    203                                 nolib = true;                                                   // strip the nodebug flag
    204204                        } else if ( arg == "-quiet" ) {
    205205                                quiet = true;                                                   // strip the quiet flag
    206206                        } else if ( arg == "-noquiet" ) {
    207207                                quiet = false;                                                  // strip the noquiet flag
     208                        } else if ( arg == "-no-include-stdhdr" ) {
     209                                noincstd_flag = true;                                   // strip the no-include-stdhdr flag
     210                        } else if ( arg == "-nolib" ) {
     211                                nolib = true;                                                   // strip the nolib flag
    208212                        } else if ( arg == "-help" ) {
    209213                                help = true;                                                    // strip the help flag
    210214                        } else if ( arg == "-nohelp" ) {
    211215                                help = false;                                                   // strip the nohelp flag
    212                         } else if ( arg == "-no-include-stdhdr" ) {
    213                                 noincstd_flag = true;                                   // strip the no-include-stdhdr flag
    214216                        } else if ( arg == "-cfalib") {
    215217                                compiling_libs = true;
     
    225227                        } else if ( arg == "-v" ) {
    226228                                verbose = true;                                                 // verbosity required
    227                                 args[nargs++] = argv[i];                                // pass argument along
     229                                args[nargs++] = argv[i];                                // pass flag along
    228230                        } else if ( arg == "-g" ) {
    229231                                debugging = true;                                               // symbolic debugging required
    230                                 args[nargs++] = argv[i];                                // pass argument along
    231                         } else if ( arg == "-save-temps" ) {
    232                                 args[nargs++] = argv[i];                                // pass argument along
     232                                args[nargs++] = argv[i];                                // pass flag along
     233                        } else if ( arg == "-save-temps" || arg == "--save-temps" ) {
     234                                args[nargs++] = argv[i];                                // pass flag along
    233235                                Putenv( argv, arg );                                    // save cfa-cpp output
    234236                        } else if ( prefix( arg, "-x" ) ) {                     // file suffix ?
    235237                                string lang;
    236                                 args[nargs++] = argv[i];                                // pass argument along
     238                                args[nargs++] = argv[i];                                // pass flag along
    237239                                if ( arg.length() == 2 ) {                              // separate argument ?
    238240                                        i += 1;
     
    243245                                        lang = arg.substr( 2 );
    244246                                } // if
    245                                 x_flag = lang != "none";
     247                                if ( x_flag ) {
     248                                        cerr << argv[0] << " warning, only one -x flag per compile, ignoring subsequent flag." << endl;
     249                                } else {
     250                                        x_flag = true;
     251                                        Putenv( argv, string( "-x=" ) + lang );
     252                                } // if
    246253                        } else if ( prefix( arg, "-std=" ) || prefix( arg, "--std=" ) ) {
    247254                                std_flag = true;                                                // -std=XX provided
    248                                 args[nargs++] = argv[i];                                // pass argument along
     255                                args[nargs++] = argv[i];                                // pass flag along
    249256                        } else if ( arg == "-w" ) {
    250                                 args[nargs++] = argv[i];                                // pass argument along
     257                                args[nargs++] = argv[i];                                // pass flag along
    251258                                Putenv( argv, arg );
    252259                        } else if ( prefix( arg, "-W" ) ) {                     // check before next tests
    253260                                if ( arg == "-Werror" || arg == "-Wall" ) {
    254                                         args[nargs++] = argv[i];                        // pass argument along
     261                                        args[nargs++] = argv[i];                        // pass flag along
    255262                                        Putenv( argv, argv[i] );
    256263                                } else {
     
    266273                                bprefix = arg.substr(2);                                // strip the -B flag
    267274                        } else if ( arg == "-c" || arg == "-S" || arg == "-E" || arg == "-M" || arg == "-MM" ) {
    268                                 args[nargs++] = argv[i];                                // pass argument along
     275                                args[nargs++] = argv[i];                                // pass flag along
    269276                                if ( arg == "-E" || arg == "-M" || arg == "-MM" ) {
    270277                                        cpp_flag = true;                                        // cpp only
    271278                                } // if
    272279                                link = false;                           // no linkage required
     280                        } else if ( arg == "-D" || arg == "-U" || arg == "-I" || arg == "-MF" || arg == "-MT" || arg == "-MQ" ||
     281                                                arg == "-include" || arg == "-imacros" || arg == "-idirafter" || arg == "-iprefix" ||
     282                                                arg == "-iwithprefix" || arg == "-iwithprefixbefore" || arg == "-isystem" || arg == "-isysroot" ) {
     283                                args[nargs++] = argv[i];                                // pass flag along
     284                                i += 1;
     285                                args[nargs++] = argv[i];                                // pass argument along
    273286                        } else if ( arg[1] == 'l' ) {
    274287                                // if the user specifies a library, load it after user code
     
    302315
    303316        #ifdef __x86_64__
    304         args[nargs++] = "-mcx16";                                                       // allow double-wide CAA
     317        args[nargs++] = "-mcx16";                                                       // allow double-wide CAS
    305318        #endif // __x86_64__
    306319
     
    322335        string libbase;
    323336        switch(path) {
    324         case Installed:
     337          case Installed:
    325338                args[nargs++] = "-I" CFA_INCDIR;
    326339                // do not use during build
     
    332345                libbase = CFA_LIBDIR;
    333346                break;
    334         case BuildTree:
    335         case Distributed:
     347          case BuildTree:
     348          case Distributed:
    336349                args[nargs++] = "-I" TOP_SRCDIR "libcfa/src";
    337350                // do not use during build
     
    367380        string libdir = libbase + arch + "-" + config;
    368381
    369         if (path != Distributed) {
     382        if ( path != Distributed ) {
    370383                if ( ! nolib && ! dirExists( libdir ) ) {
    371384                        cerr << argv[0] << " internal error, configuration " << config << " not installed." << endl;
     
    385398        } // if
    386399
     400        string preludedir;
    387401        switch(path) {
    388         case Installed   : Putenv( argv, "--prelude-dir=" + libdir ); break;
    389         case BuildTree   : Putenv( argv, "--prelude-dir=" + libdir + "/prelude" ); break;
    390         case Distributed : Putenv( argv, "--prelude-dir=" + dir(argv[0]) ); break;
    391         }
     402          case Installed   : preludedir = libdir; break;
     403          case BuildTree   : preludedir = libdir + "/prelude"; break;
     404          case Distributed : preludedir = dir(argv[0]); break;
     405        } // switch
     406
     407        Putenv( argv, "--prelude-dir=" + preludedir );
     408        args[nargs++] = "-include";
     409        args[nargs++] = (*new string(preludedir + "/defines.hfa")).c_str();
    392410
    393411        for ( int i = 0; i < nlibs; i += 1 ) {                          // copy non-user libraries after all user libraries
     
    415433                args[nargs++] = "-Wl,--pop-state";
    416434                args[nargs++] = "-pthread";
     435                #if defined(  __x86_64__ ) || defined( __ARM_ARCH )
     436                args[nargs++] = "-latomic";                                             // allow double-wide CAS
     437                #endif // __x86_64__
    417438                args[nargs++] = "-ldl";
    418                 args[nargs++] = "-lrt";
    419439                args[nargs++] = "-lm";
    420440        } // if
     
    454474        if ( bprefix.length() == 0 ) {
    455475                switch(path) {
    456                 case Installed   : bprefix = installlibdir; break;
    457                 case BuildTree   : bprefix = srcdriverdir ; break;
    458                 case Distributed : bprefix = dir(argv[0]) ; break;
    459                 }
    460                 if ( bprefix[bprefix.length() - 1] != '/' ) bprefix += '/';
    461                 Putenv( argv, string("-B=") + bprefix );
    462         } // if
     476                  case Installed   : bprefix = installlibdir; break;
     477                  case BuildTree   : bprefix = srcdriverdir ; break;
     478                  case Distributed : bprefix = dir(argv[0]) ; break;
     479                } // switch
     480        } // if
     481        if ( bprefix[bprefix.length() - 1] != '/' ) bprefix += '/';
     482        Putenv( argv, string("-B=") + bprefix );
    463483
    464484        args[nargs++] = "-Xlinker";                                                     // used by backtrace
     
    482502                args[nargs++] = "-Wno-cast-function-type";
    483503                #endif // HAVE_CAST_FUNCTION_TYPE
    484                 if ( ! std_flag ) {                                                             // default c11, if none specified
    485                         args[nargs++] = "-std=gnu11";
     504                if ( ! std_flag && ! x_flag ) {
     505                        args[nargs++] = "-std=gnu11";                           // default c11, if none specified
    486506                } // if
    487507                args[nargs++] = "-fgnu89-inline";
     
    533553        // execute the command and return the result
    534554
    535         execvp( args[0], (char *const *)args );                         // should not return
     555        execvp( args[0], (char * const *)args );                        // should not return
    536556        perror( "CFA Translator error: execvp" );
    537557        exit( EXIT_FAILURE );
  • libcfa/configure.ac

    r3c64c668 r58fe85a  
    33
    44AC_PREREQ([2.68])
    5 AC_INIT([cfa-cc],[1.0.0.0],[cforall@plg.uwaterloo.ca])
     5AC_INIT([cfa-cc],[1.0.0],[cforall@plg.uwaterloo.ca])
    66AC_CONFIG_AUX_DIR([automake])
    77AC_CONFIG_MACRO_DIRS([automake])
    88AM_SILENT_RULES([yes])
    99
    10 m4_include([../automake/cfa.m4])
     10m4_include([../tools/build/cfa.m4])
    1111
    1212AM_INIT_AUTOMAKE([subdir-objects])
     
    3030        [  --enable-distcc     whether or not to enable distributed compilation],
    3131        enable_distcc=$enableval, enable_distcc=no)
     32
     33AC_ARG_WITH(bwlimit,
     34        [  --with-bwlimit=RATE     RATE the maximum rate at which rsync will be limited when using distributed builds],
     35        DIST_BWLIMIT=$withval, DIST_BWLIMIT=0)
    3236
    3337echo -n "checking for distributated build... "
     
    5559AC_SUBST(CFADIR_HASH)
    5660AC_SUBST(CFA_VERSION)
     61AC_SUBST(DIST_BWLIMIT)
    5762
    5863#==============================================================================
     
    100105AM_CONDITIONAL([BUILDLIB], [test "x${CONFIG_BUILDLIB}" = "xyes"])
    101106
     107AM_T='$(T)'
     108AC_SUBST(AM_T)
     109
    102110#==============================================================================
    103111#Trasforming cc1 will break compilation
     
    109117
    110118# Checks for programs.
    111 LT_INIT
     119LT_INIT([disable-static])
    112120
    113121AC_PROG_CXX
     
    118126AC_PROG_MAKE_SET
    119127
     128
     129
     130#io_uring 5.4 and earlier uses defines
     131#io_uring 5.5 uses enum values
     132#io_uring 5.6 and later uses probes
     133
     134AH_TEMPLATE([CFA_HAVE_LINUX_IO_URING_H],[Defined if io_uring support is present when compiling libcfathread.])
     135AH_TEMPLATE([CFA_HAVE_IORING_OP_NOP],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_NOP.])
     136AH_TEMPLATE([CFA_HAVE_IORING_OP_READV],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_READV.])
     137AH_TEMPLATE([CFA_HAVE_IORING_OP_WRITEV],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_WRITEV.])
     138AH_TEMPLATE([CFA_HAVE_IORING_OP_FSYNC],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_FSYNC.])
     139AH_TEMPLATE([CFA_HAVE_IORING_OP_READ_FIXED],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_READ_FIXED.])
     140AH_TEMPLATE([CFA_HAVE_IORING_OP_WRITE_FIXED],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_WRITE_FIXED.])
     141AH_TEMPLATE([CFA_HAVE_IORING_OP_POLL_ADD],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_POLL_ADD.])
     142AH_TEMPLATE([CFA_HAVE_IORING_OP_POLL_REMOVE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_POLL_REMOVE.])
     143AH_TEMPLATE([CFA_HAVE_IORING_OP_SYNC_FILE_RANGE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_SYNC_FILE_RANGE.])
     144AH_TEMPLATE([CFA_HAVE_IORING_OP_SENDMSG],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_SENDMSG.])
     145AH_TEMPLATE([CFA_HAVE_IORING_OP_RECVMSG],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_RECVMSG.])
     146AH_TEMPLATE([CFA_HAVE_IORING_OP_TIMEOUT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_TIMEOUT.])
     147AH_TEMPLATE([CFA_HAVE_IORING_OP_TIMEOUT_REMOVE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_TIMEOUT_REMOVE.])
     148AH_TEMPLATE([CFA_HAVE_IORING_OP_ACCEPT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_ACCEPT.])
     149AH_TEMPLATE([CFA_HAVE_IORING_OP_ASYNC_CANCEL],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_ASYNC_CANCEL.])
     150AH_TEMPLATE([CFA_HAVE_IORING_OP_LINK_TIMEOUT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_LINK_TIMEOUT.])
     151AH_TEMPLATE([CFA_HAVE_IORING_OP_CONNECT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_CONNECT.])
     152AH_TEMPLATE([CFA_HAVE_IORING_OP_FALLOCATE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_FALLOCATE.])
     153AH_TEMPLATE([CFA_HAVE_IORING_OP_OPENAT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_OPENAT.])
     154AH_TEMPLATE([CFA_HAVE_IORING_OP_CLOSE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_CLOSE.])
     155AH_TEMPLATE([CFA_HAVE_IORING_OP_FILES_UPDATE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_FILES_UPDATE.])
     156AH_TEMPLATE([CFA_HAVE_IORING_OP_STATX],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_STATX.])
     157AH_TEMPLATE([CFA_HAVE_IORING_OP_READ],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_READ.])
     158AH_TEMPLATE([CFA_HAVE_IORING_OP_WRITE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_WRITE.])
     159AH_TEMPLATE([CFA_HAVE_IORING_OP_FADVISE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_FADVISE.])
     160AH_TEMPLATE([CFA_HAVE_IORING_OP_MADVISE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_MADVISE.])
     161AH_TEMPLATE([CFA_HAVE_IORING_OP_SEND],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_SEND.])
     162AH_TEMPLATE([CFA_HAVE_IORING_OP_RECV],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_RECV.])
     163AH_TEMPLATE([CFA_HAVE_IORING_OP_OPENAT2],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_OPENAT2.])
     164AH_TEMPLATE([CFA_HAVE_IORING_OP_EPOLL_CTL],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_EPOLL_CTL.])
     165AH_TEMPLATE([CFA_HAVE_IORING_OP_SPLICE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_SPLICE.])
     166AH_TEMPLATE([CFA_HAVE_IORING_OP_PROVIDE_BUFFERS],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_PROVIDE_BUFFERS.])
     167AH_TEMPLATE([CFA_HAVE_IORING_OP_REMOVE_BUFFER],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_REMOVE_BUFFER.])
     168AH_TEMPLATE([CFA_HAVE_IORING_OP_TEE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_TEE.])
     169AH_TEMPLATE([CFA_HAVE_IOSQE_FIXED_FILE],[Defined if io_uring support is present when compiling libcfathread and supports the flag FIXED_FILE.])
     170AH_TEMPLATE([CFA_HAVE_IOSQE_IO_DRAIN],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_DRAIN.])
     171AH_TEMPLATE([CFA_HAVE_IOSQE_ASYNC],[Defined if io_uring support is present when compiling libcfathread and supports the flag ASYNC.])
     172AH_TEMPLATE([CFA_HAVE_IOSQE_IO_LINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_LINK.])
     173AH_TEMPLATE([CFA_HAVE_IOSQE_IO_HARDLINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_HARDLINK.])
     174AH_TEMPLATE([CFA_HAVE_SPLICE_F_FD_IN_FIXED],[Defined if io_uring support is present when compiling libcfathread and supports the flag SPLICE_F_FD_IN_FIXED.])
     175AH_TEMPLATE([CFA_HAVE_IORING_SETUP_ATTACH_WQ],[Defined if io_uring support is present when compiling libcfathread and supports the flag IORING_SETUP_ATTACH_WQ.])
     176AH_TEMPLATE([CFA_HAVE_PREADV2],[Defined if preadv2 support is present when compiling libcfathread.])
     177AH_TEMPLATE([CFA_HAVE_PWRITEV2],[Defined if pwritev2 support is present when compiling libcfathread.])
     178AH_TEMPLATE([CFA_HAVE_PWRITEV2],[Defined if pwritev2 support is present when compiling libcfathread.])
     179AH_TEMPLATE([CFA_HAVE_STATX],[Defined if statx support is present when compiling libcfathread.])
     180AH_TEMPLATE([CFA_HAVE_OPENAT2],[Defined if openat2 support is present when compiling libcfathread.])
     181AH_TEMPLATE([__CFA_NO_STATISTICS__],[Defined if libcfathread was compiled without support for statistics.])
     182
     183define(ioring_ops, [IORING_OP_NOP,IORING_OP_READV,IORING_OP_WRITEV,IORING_OP_FSYNC,IORING_OP_READ_FIXED,IORING_OP_WRITE_FIXED,IORING_OP_POLL_ADD,IORING_OP_POLL_REMOVE,IORING_OP_SYNC_FILE_RANGE,IORING_OP_SENDMSG,IORING_OP_RECVMSG,IORING_OP_TIMEOUT,IORING_OP_TIMEOUT_REMOVE,IORING_OP_ACCEPT,IORING_OP_ASYNC_CANCEL,IORING_OP_LINK_TIMEOUT,IORING_OP_CONNECT,IORING_OP_FALLOCATE,IORING_OP_OPENAT,IORING_OP_CLOSE,IORING_OP_FILES_UPDATE,IORING_OP_STATX,IORING_OP_READ,IORING_OP_WRITE,IORING_OP_FADVISE,IORING_OP_MADVISE,IORING_OP_SEND,IORING_OP_RECV,IORING_OP_OPENAT2,IORING_OP_EPOLL_CTL,IORING_OP_SPLICE,IORING_OP_PROVIDE_BUFFERS,IORING_OP_REMOVE_BUFFER,IORING_OP_TEE])
     184define(ioring_flags, [IOSQE_FIXED_FILE,IOSQE_IO_DRAIN,IOSQE_ASYNC,IOSQE_IO_LINK,IOSQE_IO_HARDLINK,SPLICE_F_FD_IN_FIXED,IORING_SETUP_ATTACH_WQ])
     185
     186define(ioring_from_decls, [
     187        m4_foreach([op], [ioring_ops], [
     188                AC_CHECK_DECL(op, [AC_DEFINE([CFA_HAVE_]op)], [], [[#include <linux/io_uring.h>]])
     189        ])
     190])
     191
     192AC_CHECK_HEADERS([linux/io_uring.h], [
     193        AC_DEFINE(CFA_HAVE_LINUX_IO_URING_H)
     194        AC_CHECK_HEADER([liburing.h], [
     195                AC_CHECK_LIB([uring], [io_uring_get_probe], [
     196                        m4_foreach([op], [ioring_ops], [
     197                                AC_CHECK_DECL(op, [
     198                                        AC_RUN_IFELSE([
     199                                                AC_LANG_PROGRAM(
     200                                                        [[#include <liburing.h>]],
     201                                                        [[int main() {]]
     202                                                        [[      struct io_uring_probe *probe = io_uring_get_probe();]]
     203                                                        [[      if(io_uring_opcode_supported(probe, ]]op[[))]]
     204                                                        [[              return 0;]]
     205                                                        [[      else]]
     206                                                        [[              return 1;]]
     207                                                        [[}]]
     208                                                )
     209                                        ],[
     210                                                AC_DEFINE([CFA_HAVE_]op)
     211                                        ],[
     212                                                AC_MSG_FAILURE([Check support for] op [ with liburing failed])
     213                                        ])
     214                                ], [], [[#include <linux/io_uring.h>]])
     215                        ])
     216                ], [
     217                        ioring_from_decls
     218                ])
     219        ], [
     220                ioring_from_decls
     221        ])
     222
     223        # check support for various io_uring flags
     224        m4_foreach([op], [ioring_flags], [
     225                AC_CHECK_DECL(op, [AC_DEFINE([CFA_HAVE_]op)], [], [[#include <linux/io_uring.h>]])
     226        ])
     227])
     228AC_CHECK_FUNC([preadv2], [AC_DEFINE([CFA_HAVE_PREADV2])])
     229AC_CHECK_FUNC([pwritev2], [AC_DEFINE([CFA_HAVE_PWRITEV2])])
     230
    120231AC_CONFIG_FILES([
    121232        Makefile
     
    123234        prelude/Makefile
    124235        ])
     236AC_CONFIG_FILES([src/concurrency/io/call.cfa], [python3 ${srcdir}/src/concurrency/io/call.cfa.in > src/concurrency/io/call.cfa])
     237
     238AC_CONFIG_HEADERS(prelude/defines.hfa)
    125239
    126240AC_OUTPUT()
  • libcfa/prelude/Makefile.am

    r3c64c668 r58fe85a  
    2121# put into lib for now
    2222cfalibdir = ${CFA_LIBDIR}
    23 cfalib_DATA = gcc-builtins.cf builtins.cf extras.cf prelude.cfa bootloader.c
     23cfalib_DATA = gcc-builtins.cf builtins.cf extras.cf prelude.cfa bootloader.c defines.hfa
     24
     25EXTRA_DIST = bootloader.cf builtins.c builtins.def extras.c extras.regx extras.regx2 prelude-gen.cc prototypes.awk prototypes.c prototypes.sed sync-builtins.cf
    2426
    2527CC = @LOCAL_CFACC@
     
    6870
    6971MOSTLYCLEANFILES = bootloader.c builtins.cf extras.cf gcc-builtins.c gcc-builtins.cf prelude.cfa
     72DISTCLEANFILES = $(DEPDIR)/builtins.Po
    7073MAINTAINERCLEANFILES = ${addprefix ${libdir}/,${cfalib_DATA}} ${addprefix ${libdir}/,${lib_LIBRARIES}}
    7174
    7275if ENABLE_DISTCC
    7376distribution: @LOCAL_CFACC@ @LOCAL_CC1@ @CFACPP@ gcc-builtins.cf builtins.cf extras.cf prelude.cfa bootloader.c $(srcdir)/../../tools/build/push2dist.sh
    74         ${AM_V_GEN}$(srcdir)/../../tools/build/push2dist.sh @CFADIR_HASH@
     77        ${AM_V_GEN}$(srcdir)/../../tools/build/push2dist.sh @CFADIR_HASH@ @DIST_BWLIMIT@
    7578        @echo "Dummy file to track distribution to remote hosts" > ${@}
    7679
  • libcfa/prelude/bootloader.cf

    r3c64c668 r58fe85a  
    11extern "C" { static inline int invoke_main(int argc, char* argv[], char* envp[]); }
     2int cfa_args_argc;
     3char ** cfa_args_argv;
     4char ** cfa_args_envp;
    25
    36int main(int argc, char* argv[], char* envp[]) {
     7        cfa_args_argc = argc;
     8        cfa_args_argv = argv;
     9        cfa_args_envp = envp;
    410        return invoke_main(argc, argv, envp);
    511}
  • libcfa/prelude/builtins.c

    r3c64c668 r58fe85a  
    99// Author           : Peter A. Buhr
    1010// Created On       : Fri Jul 21 16:21:03 2017
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Nov 21 16:31:39 2019
    13 // Update Count     : 101
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Oct 27 14:42:00 2020
     13// Update Count     : 111
    1414//
     15
     16#define __cforall_builtins__
    1517
    1618// type that wraps a pointer and a destructor-like function - used in generating implicit destructor calls for struct members in user-defined functions
     
    4951void abort( const char fmt[], ... ) __attribute__ (( format(printf, 1, 2), __nothrow__, __leaf__, __noreturn__ ));
    5052
     53forall(dtype T)
     54static inline T & identity(T & i) {
     55        return i;
     56}
     57
     58// generator support
     59struct $generator {
     60        inline int;
     61};
     62
     63static inline void  ?{}($generator & this) { ((int&)this) = 0; }
     64static inline void ^?{}($generator &) {}
     65
     66trait is_generator(dtype T) {
     67      void main(T & this);
     68      $generator * get_generator(T & this);
     69};
     70
     71forall(dtype T | is_generator(T))
     72static inline T & resume(T & gen) {
     73        main(gen);
     74        return gen;
     75}
     76
    5177// implicit increment, decrement if += defined, and implicit not if != defined
    5278
     
    7096// universal typed pointer constant
    7197static inline forall( dtype DT ) DT * intptr( uintptr_t addr ) { return (DT *)addr; }
     98static inline forall( ftype FT ) FT * intptr( uintptr_t addr ) { return (FT *)addr; }
     99
     100#if defined(__SIZEOF_INT128__)
     101// constructor for 128-bit numbers (all constants are unsigned as +/- are operators)
     102static inline void ?{}( unsigned int128 & this, unsigned long int h, unsigned long int l ) {
     103        this = (unsigned int128)h << 64 | (unsigned int128)l;
     104} // ?{}
     105#endif // __SIZEOF_INT128__
    72106
    73107// exponentiation operator implementation
  • libcfa/src/Makefile.am

    r3c64c668 r58fe85a  
    1111## Created On       : Sun May 31 08:54:01 2015
    1212## Last Modified By : Peter A. Buhr
    13 ## Last Modified On : Mon Jul 15 22:43:27 2019
    14 ## Update Count     : 241
     13## Last Modified On : Wed Dec  9 22:46:14 2020
     14## Update Count     : 250
    1515###############################################################################
    1616
     
    1919ACLOCAL_AMFLAGS  = -I automake
    2020
    21 include $(srcdir)/../../src/cfa.make
     21include $(top_srcdir)/../tools/build/cfa.make
    2222
    2323libdir = ${CFA_LIBDIR}
     
    3131# AM_CFAFLAGS for only cfa source
    3232# use -no-include-stdhdr to prevent rebuild cycles
    33 # The built sources must not depend on the installed headers
    34 AM_CFAFLAGS = -quiet -cfalib -I$(srcdir)/stdhdr $(if $(findstring ${gdbwaittarget}, ${@}), -XCFA --gdb) @CONFIG_CFAFLAGS@
    35 AM_CFLAGS = -g -Wall -Wno-unused-function -fPIC -pthread @ARCH_FLAGS@ @CONFIG_CFLAGS@
     33# The built sources must not depend on the installed inst_headers_src
     34AM_CFAFLAGS = -quiet -cfalib -I$(srcdir)/stdhdr -I$(srcdir)/concurrency $(if $(findstring ${gdbwaittarget}, ${@}), -XCFA --gdb) @CONFIG_CFAFLAGS@
     35AM_CFLAGS = -g -Wall -Wno-unused-function -fPIC -fexceptions -pthread @ARCH_FLAGS@ @CONFIG_CFLAGS@
    3636AM_CCASFLAGS = -g -Wall -Wno-unused-function @ARCH_FLAGS@ @CONFIG_CFLAGS@
    3737CFACC = @CFACC@
     
    3939#----------------------------------------------------------------------------------------------------------------
    4040if BUILDLIB
    41 headers_nosrc = math.hfa gmp.hfa time_t.hfa bits/align.hfa bits/containers.hfa bits/defs.hfa bits/debug.hfa bits/locks.hfa
    42 headers = fstream.hfa iostream.hfa iterator.hfa limits.hfa rational.hfa time.hfa stdlib.hfa common.hfa \
    43           containers/maybe.hfa containers/pair.hfa containers/result.hfa containers/vector.hfa
    44 
    45 libsrc = startup.cfa interpose.cfa bits/debug.cfa assert.cfa exception.c virtual.c heap.cfa ${headers:.hfa=.cfa}
     41inst_headers_nosrc = \
     42        bitmanip.hfa \
     43        clock.hfa \
     44        exception.hfa \
     45        exception.h \
     46        gmp.hfa \
     47        math.hfa \
     48        time_t.hfa \
     49        bits/align.hfa \
     50        bits/containers.hfa \
     51        bits/debug.hfa \
     52        bits/defs.hfa \
     53        bits/locks.hfa \
     54        bits/collection.hfa \
     55        bits/stack.hfa \
     56        bits/queue.hfa \
     57        bits/sequence.hfa \
     58        concurrency/iofwd.hfa \
     59        containers/list.hfa \
     60        containers/stackLockFree.hfa \
     61        vec/vec.hfa \
     62        vec/vec2.hfa \
     63        vec/vec3.hfa \
     64        vec/vec4.hfa
     65
     66inst_headers_src = \
     67        common.hfa \
     68        fstream.hfa \
     69        heap.hfa \
     70        iostream.hfa \
     71        iterator.hfa \
     72        limits.hfa \
     73        memory.hfa \
     74        parseargs.hfa \
     75        rational.hfa \
     76        stdlib.hfa \
     77        time.hfa \
     78        containers/maybe.hfa \
     79        containers/pair.hfa \
     80        containers/result.hfa \
     81        containers/vector.hfa
     82
     83libsrc = ${inst_headers_src} ${inst_headers_src:.hfa=.cfa} \
     84        assert.cfa \
     85        bits/algorithm.hfa \
     86        bits/debug.cfa \
     87        exception.c \
     88        interpose.cfa \
     89        lsda.h \
     90        startup.cfa \
     91        startup.hfa \
     92        virtual.c \
     93        virtual.h
    4694
    4795# not all platforms support concurrency, add option do disable it
    48 thread_headers_nosrc = concurrency/invoke.h
    49 thread_headers = concurrency/coroutine.hfa concurrency/thread.hfa concurrency/kernel.hfa concurrency/monitor.hfa concurrency/mutex.hfa
    50 thread_libsrc = concurrency/CtxSwitch-@ARCHITECTURE@.S concurrency/alarm.cfa concurrency/invoke.c concurrency/preemption.cfa ${thread_headers:.hfa=.cfa}
     96inst_thread_headers_nosrc = \
     97        bits/random.hfa \
     98        concurrency/clib/cfathread.h \
     99        concurrency/invoke.h \
     100        concurrency/future.hfa \
     101        concurrency/kernel/fwd.hfa
     102
     103inst_thread_headers_src = \
     104        concurrency/coroutine.hfa \
     105        concurrency/exception.hfa \
     106        concurrency/kernel.hfa \
     107        concurrency/locks.hfa \
     108        concurrency/monitor.hfa \
     109        concurrency/mutex.hfa \
     110        concurrency/thread.hfa
     111
     112thread_libsrc = ${inst_thread_headers_src} ${inst_thread_headers_src:.hfa=.cfa} \
     113        bits/signal.hfa \
     114        concurrency/alarm.cfa \
     115        concurrency/alarm.hfa \
     116        concurrency/clib/cfathread.cfa \
     117        concurrency/CtxSwitch-@ARCHITECTURE@.S \
     118        concurrency/invoke.c \
     119        concurrency/io.cfa \
     120        concurrency/io/setup.cfa \
     121        concurrency/io/types.hfa \
     122        concurrency/io/call.cfa \
     123        concurrency/iofwd.hfa \
     124        concurrency/kernel_private.hfa \
     125        concurrency/kernel/startup.cfa \
     126        concurrency/preemption.cfa \
     127        concurrency/preemption.hfa \
     128        concurrency/ready_queue.cfa \
     129        concurrency/ready_subqueue.hfa \
     130        concurrency/snzi.hfa \
     131        concurrency/stats.cfa \
     132        concurrency/stats.hfa \
     133        concurrency/stats.hfa
     134
    51135else
    52 headers =
    53 thread_headers =
    54 headers_nosrc =
    55 thread_headers_nosrc =
     136inst_headers_src =
     137inst_thread_headers_src =
     138inst_headers_nosrc =
     139inst_thread_headers_nosrc =
    56140libsrc =
    57141endif
     
    96180
    97181prelude.o : prelude.cfa extras.cf gcc-builtins.cf builtins.cf @LOCAL_CFACC@ @CFACPP@
    98         ${AM_V_GEN}$(CFACOMPILE) -quiet -XCFA -l ${<} -c -o ${@}
     182        ${AM_V_GEN}$(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@}
    99183
    100184prelude.lo: prelude.cfa extras.cf gcc-builtins.cf builtins.cf @LOCAL_CFACC@ @CFACPP@
    101185        ${AM_V_GEN}$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile \
    102         $(CFACOMPILE) -quiet -XCFA -l ${<} -c -o ${@}
    103 
    104 #----------------------------------------------------------------------------------------------------------------
    105 libcfa_la_SOURCES = prelude.cfa ${libsrc}
     186        $(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@}
     187
     188#----------------------------------------------------------------------------------------------------------------
     189libcfa_la_SOURCES = ${libsrc}
     190nodist_libcfa_la_SOURCES = prelude.cfa
    106191libcfa_la_LDFLAGS = -version-info @CFA_VERSION@
    107192
     
    112197
    113198cfa_includedir = $(CFA_INCDIR)
    114 nobase_cfa_include_HEADERS = ${stdhdr} ${headers} ${headers_nosrc} ${thread_headers} ${thread_headers_nosrc}
     199nobase_cfa_include_HEADERS = ${stdhdr} ${inst_headers_src} ${inst_headers_nosrc} ${inst_thread_headers_src} ${inst_thread_headers_nosrc}
     200EXTRA_DIST = stdhdr
    115201
    116202#----------------------------------------------------------------------------------------------------------------
    117203maintainer-clean-local:
    118204        -rm -rf ${CFA_INCDIR} ${CFA_LIBDIR}
     205
     206distclean-local:
     207        find ${builddir} -path '*.Plo' -delete
    119208
    120209
  • libcfa/src/bits/containers.hfa

    r3c64c668 r58fe85a  
    1717#include "bits/align.hfa"
    1818#include "bits/defs.hfa"
    19 
     19#include <stdio.h>
    2020//-----------------------------------------------------------------------------
    2121// Array
     
    3636        #define __small_array_t(T) __small_array(T)
    3737#else
    38         #define __small_array_t(T) struct __small_array
     38        #define __small_array_t(T) __small_array
    3939#endif
    4040
     
    146146        static inline forall( dtype T | is_node(T) ) {
    147147                void ?{}( __queue(T) & this ) with( this ) {
    148                         head{ 1p };
    149                         tail{ &head };
    150                         verify(*tail == 1p);
     148                        (this.head){ 1p };
     149                        (this.tail){ &this.head };
     150                        verify(*this.tail == 1p);
    151151                }
    152152
    153153                void append( __queue(T) & this, T * val ) with( this ) {
    154                         verify(tail != 0p);
    155                         verify(*tail == 1p);
    156                         *tail = val;
    157                         tail = &get_next( *val );
    158                         *tail = 1p;
     154                        verify(this.tail != 0p);
     155                        verify(*this.tail == 1p);
     156                        *this.tail = val;
     157                        this.tail = &get_next( *val );
     158                        *this.tail = 1p;
     159                }
     160
     161                T * peek( __queue(T) & this ) {
     162                        verify(*this.tail == 1p);
     163                        T * front = this.head;
     164                        if( front != 1p ) {
     165                                verify(*this.tail == 1p);
     166                                return front;
     167                        }
     168                        verify(*this.tail == 1p);
     169                        return 0p;
    159170                }
    160171
    161172                T * pop_head( __queue(T) & this ) {
    162173                        verify(*this.tail == 1p);
    163                         T * head = this.head;
    164                         if( head != 1p ) {
    165                                 this.head = get_next( *head );
    166                                 if( get_next( *head ) == 1p ) {
     174                        T * _head = this.head;
     175                        if( _head != 1p ) {
     176                                this.head = get_next( *_head );
     177                                if( get_next( *_head ) == 1p ) {
    167178                                        this.tail = &this.head;
    168179                                }
    169                                 get_next( *head ) = 0p;
     180                                get_next( *_head ) = 0p;
    170181                                verify(*this.tail == 1p);
    171                                 return head;
     182                                verify( get_next(*_head) == 0p );
     183                                return _head;
    172184                        }
    173185                        verify(*this.tail == 1p);
     
    181193                        (*it) = get_next( *val );
    182194
    183                         if( tail == &get_next( *val ) ) {
    184                                 tail = it;
     195                        if( this.tail == &get_next( *val ) ) {
     196                                this.tail = it;
    185197                        }
    186198
    187199                        get_next( *val ) = 0p;
    188200
    189                         verify( (head == 1p) == (&head == tail) );
    190                         verify( *tail == 1p );
     201                        verify( (this.head == 1p) == (&this.head == this.tail) );
     202                        verify( *this.tail == 1p );
    191203                        return val;
    192204                }
    193205
    194206                int ?!=?( const __queue(T) & this, __attribute__((unused)) zero_t zero ) {
    195                         return this.head != 0;
     207                        return this.head != 1p;
    196208                }
    197209        }
     
    227239        forall(dtype T )
    228240        static inline [void] ?{}( __dllist(T) & this, * [T * & next, T * & prev] ( T & ) __get ) {
    229                 this.head{ 0p };
     241                (this.head){ 0p };
    230242                this.__get = __get;
    231243        }
     
    236248                void push_front( __dllist(T) & this, T & node ) with( this ) {
    237249                        verify(__get);
    238                         if ( head ) {
    239                                 __get( node ).next = head;
    240                                 __get( node ).prev = __get( *head ).prev;
     250                        if ( this.head ) {
     251                                __get( node ).next = this.head;
     252                                __get( node ).prev = __get( *this.head ).prev;
    241253                                // inserted node must be consistent before it is seen
    242254                                // prevent code movement across barrier
    243255                                asm( "" : : : "memory" );
    244                                 __get( *head ).prev = &node;
     256                                __get( *this.head ).prev = &node;
    245257                                T & _prev = *__get( node ).prev;
    246258                                __get( _prev ).next = &node;
     
    252264                        // prevent code movement across barrier
    253265                        asm( "" : : : "memory" );
    254                         head = &node;
     266                        this.head = &node;
    255267                }
    256268
    257269                void remove( __dllist(T) & this, T & node ) with( this ) {
    258270                        verify(__get);
    259                         if ( &node == head ) {
    260                                 if ( __get( *head ).next == head ) {
    261                                         head = 0p;
     271                        if ( &node == this.head ) {
     272                                if ( __get( *this.head ).next == this.head ) {
     273                                        this.head = 0p;
    262274                                } else {
    263                                         head = __get( *head ).next;
     275                                        this.head = __get( *this.head ).next;
    264276                                }
    265277                        }
     
    273285                        return this.head != 0;
    274286                }
     287
     288                void move_to_front( __dllist(T) & src, __dllist(T) & dst, T & node ) {
     289                        remove    (src, node);
     290                        push_front(dst, node);
     291                }
    275292        }
    276293        #undef next
  • libcfa/src/bits/debug.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Thu Mar 30 12:30:01 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 13:03:16 2020
    13 // Update Count     : 11
     12// Last Modified On : Wed Jun 17 11:07:13 2020
     13// Update Count     : 12
    1414//
    1515
    16 extern "C" {
    1716#include <stdio.h>
    1817#include <stdlib.h>
     
    2120#include <stdarg.h>
    2221#include <unistd.h>
    23 }
    2422
    2523enum { buffer_size = 4096 };
  • libcfa/src/bits/debug.hfa

    r3c64c668 r58fe85a  
    99// Author           : Thierry Delisle
    1010// Created On       : Mon Nov 28 12:27:26 2016
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 12:29:21 2020
    13 // Update Count     : 9
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Mon Apr 27 10:15:00 2020
     13// Update Count     : 10
    1414//
    1515
    1616#pragma once
     17
     18#include <assert.h>
    1719
    1820#ifdef __CFA_DEBUG__
     
    2325        #define __cfaabi_dbg_ctx_param const char caller[]
    2426        #define __cfaabi_dbg_ctx_param2 , const char caller[]
     27        #define __cfaabi_dbg_ctx_fwd caller
     28        #define __cfaabi_dbg_ctx_fwd2 , caller
    2529#else
    2630        #define __cfaabi_dbg_debug_do(...)
     
    3034        #define __cfaabi_dbg_ctx_param
    3135        #define __cfaabi_dbg_ctx_param2
     36        #define __cfaabi_dbg_ctx_fwd
     37        #define __cfaabi_dbg_ctx_fwd2
    3238#endif
    3339
     
    3642#endif
    3743        #include <stdarg.h>
    38         #include <stdio.h>
    3944
    4045        extern void __cfaabi_bits_write( int fd, const char buffer[], int len );
     
    4550        extern void __cfaabi_bits_print_vararg( int fd, const char fmt[], va_list arg );
    4651        extern void __cfaabi_bits_print_buffer( int fd, char buffer[], int buffer_size, const char fmt[], ... ) __attribute__(( format(printf, 4, 5) ));
     52
     53#if defined(__CFA_DEBUG_PRINT__) \
     54                || defined(__CFA_DEBUG_PRINT_IO__) || defined(__CFA_DEBUG_PRINT_IO_CORE__) \
     55                || defined(__CFA_DEBUG_PRINT_MONITOR__) || defined(__CFA_DEBUG_PRINT_PREEMPTION__) \
     56                || defined(__CFA_DEBUG_PRINT_RUNTIME_CORE__) || defined(__CFA_DEBUG_PRINT_EXCEPTION__) \
     57                || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
     58        #include <stdio.h>
     59        #include <unistd.h>
     60#endif
    4761#ifdef __cforall
    4862}
    4963#endif
    5064
     65// Deprecated: Use the versions with the new module names.
    5166#ifdef __CFA_DEBUG_PRINT__
    5267        #define __cfaabi_dbg_write( buffer, len )         __cfaabi_bits_write( STDERR_FILENO, buffer, len )
    5368        #define __cfaabi_dbg_acquire()                    __cfaabi_bits_acquire()
    5469        #define __cfaabi_dbg_release()                    __cfaabi_bits_release()
    55         #define __cfaabi_dbg_print_safe(...)              __cfaabi_bits_print_safe   (__VA_ARGS__)
    56         #define __cfaabi_dbg_print_nolock(...)            __cfaabi_bits_print_nolock (__VA_ARGS__)
    57         #define __cfaabi_dbg_print_buffer(...)            __cfaabi_bits_print_buffer (__VA_ARGS__)
    58         #define __cfaabi_dbg_print_buffer_decl(...)       char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( __dbg_text, __dbg_len );
    59         #define __cfaabi_dbg_print_buffer_local(...)      __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_dbg_write( __dbg_text, __dbg_len );
     70        #define __cfaabi_dbg_print_safe(...)              __cfaabi_bits_print_safe   ( STDERR_FILENO, __VA_ARGS__ )
     71        #define __cfaabi_dbg_print_nolock(...)            __cfaabi_bits_print_nolock ( STDERR_FILENO, __VA_ARGS__ )
     72        #define __cfaabi_dbg_print_buffer(...)            __cfaabi_bits_print_buffer ( STDERR_FILENO, __VA_ARGS__ )
     73        #define __cfaabi_dbg_print_buffer_decl(...)       char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( STDERR_FILENO, __dbg_text, __dbg_len );
     74        #define __cfaabi_dbg_print_buffer_local(...)      __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_dbg_write( STDERR_FILENO, __dbg_text, __dbg_len );
    6075#else
    6176        #define __cfaabi_dbg_write(...)               ((void)0)
     
    6984#endif
    7085
     86// Debug print functions and statements:
     87// Most are wrappers around the bits printing function but are not always used.
     88// If they are used depends if the group (first argument) is active or not. The group must be one
     89// defined belowe. The other arguments depend on the wrapped function.
     90#define __cfadbg_write(group, buffer, len) \
     91        __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_write(STDERR_FILENO, buffer, len))
     92#define __cfadbg_acquire(group) \
     93        __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_acquire())
     94#define __cfadbg_release(group) \
     95        __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_release())
     96#define __cfadbg_print_safe(group, ...) \
     97        __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_safe(STDERR_FILENO, __VA_ARGS__))
     98#define __cfadbg_print_nolock(group, ...) \
     99        __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_nolock(STDERR_FILENO, __VA_ARGS__))
     100#define __cfadbg_print_buffer(group, ...) \
     101        __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_buffer(STDERR_FILENO, __VA_ARGS__))
     102#define __cfadbg_print_buffer_decl(group, ...) \
     103        __CFADBG_PRINT_GROUP_##group(char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( __dbg_text, __dbg_len ))
     104#define __cfadbg_print_buffer_local(group, ...) \
     105        __CFADBG_PRINT_GROUP_##group(__dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write(STDERR_FILENO, __dbg_text, __dbg_len))
     106
     107// The debug print groups:
     108#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_IO__)
     109#       define __CFADBG_PRINT_GROUP_io(...) __VA_ARGS__
     110#else
     111#       define __CFADBG_PRINT_GROUP_io(...) ((void)0)
     112#endif
     113#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_IO__) || defined(__CFA_DEBUG_PRINT_IO_CORE__)
     114#       define __CFADBG_PRINT_GROUP_io_core(...) __VA_ARGS__
     115#else
     116#       define __CFADBG_PRINT_GROUP_io_core(...) ((void)0)
     117#endif
     118#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_MONITOR__)
     119#       define __CFADBG_PRINT_GROUP_monitor(...) __VA_ARGS__
     120#else
     121#       define __CFADBG_PRINT_GROUP_monitor(...) ((void)0)
     122#endif
     123#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_PREEMPTION__)
     124#       define __CFADBG_PRINT_GROUP_preemption(...) __VA_ARGS__
     125#else
     126#       define __CFADBG_PRINT_GROUP_preemption(...) ((void)0)
     127#endif
     128#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_RUNTIME_CORE__)
     129#       define __CFADBG_PRINT_GROUP_runtime_core(...) __VA_ARGS__
     130#else
     131#       define __CFADBG_PRINT_GROUP_runtime_core(...) ((void)0)
     132#endif
     133#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
     134#       define __CFADBG_PRINT_GROUP_ready_queue(...) __VA_ARGS__
     135#else
     136#       define __CFADBG_PRINT_GROUP_ready_queue(...) ((void)0)
     137#endif
     138#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_EXCEPTION__)
     139#       define __CFADBG_PRINT_GROUP_exception(...) __VA_ARGS__
     140#else
     141#       define __CFADBG_PRINT_GROUP_exception(...) ((void)0)
     142#endif
     143
    71144// Local Variables: //
    72145// mode: c //
  • libcfa/src/bits/defs.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Thu Nov  9 13:24:10 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Jan 28 22:38:27 2020
    13 // Update Count     : 9
     12// Last Modified On : Sat Oct 24 10:53:15 2020
     13// Update Count     : 21
    1414//
    1515
    1616#pragma once
    1717
    18 #include <stdbool.h>
    19 #include <stddef.h>
    2018#include <stdint.h>
     19#include <assert.h>
    2120
    2221#define likely(x)   __builtin_expect(!!(x), 1)
     
    3029#define __cfa_anonymous_object(x) inline struct x
    3130#else
    32 #define __cfa_anonymous_object(x) x __cfa_anonymous_object
     31#define __cfa_anonymous_object(x) struct x __cfa_anonymous_object
    3332#endif
    3433
     
    4948#endif
    5049
    51 static inline long long rdtscl(void) {
    52     unsigned int lo, hi;
    53     __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    54     return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
     50static inline long long int rdtscl(void) {
     51        #if defined( __i386 ) || defined( __x86_64 )
     52        unsigned int lo, hi;
     53        __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
     54        return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
     55        #elif defined( __aarch64__ ) || defined( __arm__ )
     56        // https://github.com/google/benchmark/blob/v1.1.0/src/cycleclock.h#L116
     57        long long int virtual_timer_value;
     58        asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
     59        return virtual_timer_value;
     60        #else
     61                #error unsupported hardware architecture
     62        #endif
    5563}
  • libcfa/src/bits/locks.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Oct 31 15:14:38 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 13:03:19 2020
    13 // Update Count     : 11
     12// Last Modified On : Wed Aug 12 14:18:07 2020
     13// Update Count     : 13
    1414//
    1515
     
    2727
    2828// pause to prevent excess processor bus usage
    29 #if defined( __sparc )
    30         #define Pause() __asm__ __volatile__ ( "rd %ccr,%g0" )
    31 #elif defined( __i386 ) || defined( __x86_64 )
     29#if defined( __i386 ) || defined( __x86_64 )
    3230        #define Pause() __asm__ __volatile__ ( "pause" : : : )
    3331#elif defined( __ARM_ARCH )
    34         #define Pause() __asm__ __volatile__ ( "nop" : : : )
     32        #define Pause() __asm__ __volatile__ ( "YIELD" : : : )
    3533#else
    3634        #error unsupported architecture
     
    5452
    5553                #ifdef __CFA_DEBUG__
    56                         void __cfaabi_dbg_record(__spinlock_t & this, const char prev_name[]);
     54                        void __cfaabi_dbg_record_lock(__spinlock_t & this, const char prev_name[]);
    5755                #else
    58                         #define __cfaabi_dbg_record(x, y)
     56                        #define __cfaabi_dbg_record_lock(x, y)
    5957                #endif
    6058        }
     
    6967                bool result = (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0);
    7068                if( result ) {
    71                         __cfaabi_dbg_record( this, caller );
     69                        __cfaabi_dbg_record_lock( this, caller );
    7270                } else {
    7371                        enable_interrupts_noPoll();
     
    9997                        #endif
    10098                }
    101                 __cfaabi_dbg_record( this, caller );
     99                __cfaabi_dbg_record_lock( this, caller );
    102100        }
    103101
     
    112110        #endif
    113111
     112        extern "C" {
     113                char * strerror(int);
     114        }
     115        #define CHECKED(x) { int err = x; if( err != 0 ) abort("KERNEL ERROR: Operation \"" #x "\" return error %d - %s\n", err, strerror(err)); }
     116
    114117        struct __bin_sem_t {
    115                 bool                    signaled;
    116118                pthread_mutex_t         lock;
    117119                pthread_cond_t          cond;
     120                int                     val;
    118121        };
    119122
    120123        static inline void ?{}(__bin_sem_t & this) with( this ) {
    121                 signaled = false;
    122                 pthread_mutex_init(&lock, NULL);
    123                 pthread_cond_init (&cond, NULL);
     124                // Create the mutex with error checking
     125                pthread_mutexattr_t mattr;
     126                pthread_mutexattr_init( &mattr );
     127                pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_ERRORCHECK_NP);
     128                pthread_mutex_init(&lock, &mattr);
     129
     130                pthread_cond_init (&cond, (const pthread_condattr_t *)0p);  // workaround trac#208: cast should not be required
     131                val = 0;
    124132        }
    125133
    126134        static inline void ^?{}(__bin_sem_t & this) with( this ) {
    127                 pthread_mutex_destroy(&lock);
    128                 pthread_cond_destroy (&cond);
     135                CHECKED( pthread_mutex_destroy(&lock) );
     136                CHECKED( pthread_cond_destroy (&cond) );
    129137        }
    130138
    131139        static inline void wait(__bin_sem_t & this) with( this ) {
    132140                verify(__cfaabi_dbg_in_kernel());
    133                 pthread_mutex_lock(&lock);
    134                         if(!signaled) {   // this must be a loop, not if!
     141                CHECKED( pthread_mutex_lock(&lock) );
     142                        while(val < 1) {
    135143                                pthread_cond_wait(&cond, &lock);
    136144                        }
    137                         signaled = false;
    138                 pthread_mutex_unlock(&lock);
    139         }
    140 
    141         static inline void post(__bin_sem_t & this) with( this ) {
    142                 verify(__cfaabi_dbg_in_kernel());
    143 
    144                 pthread_mutex_lock(&lock);
    145                         bool needs_signal = !signaled;
    146                         signaled = true;
    147                 pthread_mutex_unlock(&lock);
    148 
    149                 if (needs_signal)
    150                         pthread_cond_signal(&cond);
     145                        val -= 1;
     146                CHECKED( pthread_mutex_unlock(&lock) );
     147        }
     148
     149        static inline bool post(__bin_sem_t & this) with( this ) {
     150                bool needs_signal = false;
     151
     152                CHECKED( pthread_mutex_lock(&lock) );
     153                        if(val < 1) {
     154                                val += 1;
     155                                pthread_cond_signal(&cond);
     156                                needs_signal = true;
     157                        }
     158                CHECKED( pthread_mutex_unlock(&lock) );
     159
     160                return needs_signal;
     161        }
     162
     163        #undef CHECKED
     164
     165        struct $thread;
     166        extern void park( void );
     167        extern void unpark( struct $thread * this );
     168        static inline struct $thread * active_thread ();
     169
     170        // Semaphore which only supports a single thread
     171        struct single_sem {
     172                struct $thread * volatile ptr;
     173        };
     174
     175        static inline {
     176                void  ?{}(single_sem & this) {
     177                        this.ptr = 0p;
     178                }
     179
     180                void ^?{}(single_sem &) {}
     181
     182                bool wait(single_sem & this) {
     183                        for() {
     184                                struct $thread * expected = this.ptr;
     185                                if(expected == 1p) {
     186                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     187                                                return false;
     188                                        }
     189                                }
     190                                else {
     191                                        /* paranoid */ verify( expected == 0p );
     192                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     193                                                park();
     194                                                return true;
     195                                        }
     196                                }
     197
     198                        }
     199                }
     200
     201                bool post(single_sem & this) {
     202                        for() {
     203                                struct $thread * expected = this.ptr;
     204                                if(expected == 1p) return false;
     205                                if(expected == 0p) {
     206                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, 1p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     207                                                return false;
     208                                        }
     209                                }
     210                                else {
     211                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     212                                                unpark( expected );
     213                                                return true;
     214                                        }
     215                                }
     216                        }
     217                }
     218        }
     219
     220        // Synchronozation primitive which only supports a single thread and one post
     221        // Similar to a binary semaphore with a 'one shot' semantic
     222        // is expected to be discarded after each party call their side
     223        struct oneshot {
     224                // Internal state :
     225                //     0p     : is initial state (wait will block)
     226                //     1p     : fulfilled (wait won't block)
     227                // any thread : a thread is currently waiting
     228                struct $thread * volatile ptr;
     229        };
     230
     231        static inline {
     232                void  ?{}(oneshot & this) {
     233                        this.ptr = 0p;
     234                }
     235
     236                void ^?{}(oneshot &) {}
     237
     238                // Wait for the post, return immidiately if it already happened.
     239                // return true if the thread was parked
     240                bool wait(oneshot & this) {
     241                        for() {
     242                                struct $thread * expected = this.ptr;
     243                                if(expected == 1p) return false;
     244                                /* paranoid */ verify( expected == 0p );
     245                                if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     246                                        park();
     247                                        /* paranoid */ verify( this.ptr == 1p );
     248                                        return true;
     249                                }
     250                        }
     251                }
     252
     253                // Mark as fulfilled, wake thread if needed
     254                // return true if a thread was unparked
     255                bool post(oneshot & this) {
     256                        struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
     257                        if( got == 0p ) return false;
     258                        unpark( got );
     259                        return true;
     260                }
     261        }
     262
     263        // base types for future to build upon
     264        // It is based on the 'oneshot' type to allow multiple futures
     265        // to block on the same instance, permitting users to block a single
     266        // thread on "any of" [a given set of] futures.
     267        // does not support multiple threads waiting on the same future
     268        struct future_t {
     269                // Internal state :
     270                //     0p      : is initial state (wait will block)
     271                //     1p      : fulfilled (wait won't block)
     272                //     2p      : in progress ()
     273                //     3p      : abandoned, server should delete
     274                // any oneshot : a context has been setup to wait, a thread could wait on it
     275                struct oneshot * volatile ptr;
     276        };
     277
     278        static inline {
     279                void  ?{}(future_t & this) {
     280                        this.ptr = 0p;
     281                }
     282
     283                void ^?{}(future_t &) {}
     284
     285                void reset(future_t & this) {
     286                        // needs to be in 0p or 1p
     287                        __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);
     288                }
     289
     290                // check if the future is available
     291                bool available( future_t & this ) {
     292                        return this.ptr == 1p;
     293                }
     294
     295                // Prepare the future to be waited on
     296                // intented to be use by wait, wait_any, waitfor, etc. rather than used directly
     297                bool setup( future_t & this, oneshot & wait_ctx ) {
     298                        /* paranoid */ verify( wait_ctx.ptr == 0p );
     299                        // The future needs to set the wait context
     300                        for() {
     301                                struct oneshot * expected = this.ptr;
     302                                // Is the future already fulfilled?
     303                                if(expected == 1p) return false; // Yes, just return false (didn't block)
     304
     305                                // The future is not fulfilled, try to setup the wait context
     306                                /* paranoid */ verify( expected == 0p );
     307                                if(__atomic_compare_exchange_n(&this.ptr, &expected, &wait_ctx, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     308                                        return true;
     309                                }
     310                        }
     311                }
     312
     313                // Stop waiting on a future
     314                // When multiple futures are waited for together in "any of" pattern
     315                // futures that weren't fulfilled before the thread woke up
     316                // should retract the wait ctx
     317                // intented to be use by wait, wait_any, waitfor, etc. rather than used directly
     318                void retract( future_t & this, oneshot & wait_ctx ) {
     319                        // Remove the wait context
     320                        struct oneshot * got = __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);
     321
     322                        // got == 0p: future was never actually setup, just return
     323                        if( got == 0p ) return;
     324
     325                        // got == wait_ctx: since fulfil does an atomic_swap,
     326                        // if we got back the original then no one else saw context
     327                        // It is safe to delete (which could happen after the return)
     328                        if( got == &wait_ctx ) return;
     329
     330                        // got == 1p: the future is ready and the context was fully consumed
     331                        // the server won't use the pointer again
     332                        // It is safe to delete (which could happen after the return)
     333                        if( got == 1p ) return;
     334
     335                        // got == 2p: the future is ready but the context hasn't fully been consumed
     336                        // spin until it is safe to move on
     337                        if( got == 2p ) {
     338                                while( this.ptr != 1p ) Pause();
     339                                return;
     340                        }
     341
     342                        // got == any thing else, something wen't wrong here, abort
     343                        abort("Future in unexpected state");
     344                }
     345
     346                // Mark the future as abandoned, meaning it will be deleted by the server
     347                bool abandon( future_t & this ) {
     348                        /* paranoid */ verify( this.ptr != 3p );
     349
     350                        // Mark the future as abandonned
     351                        struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST);
     352
     353                        // If the future isn't already fulfilled, let the server delete it
     354                        if( got == 0p ) return false;
     355
     356                        // got == 2p: the future is ready but the context hasn't fully been consumed
     357                        // spin until it is safe to move on
     358                        if( got == 2p ) {
     359                                while( this.ptr != 1p ) Pause();
     360                                got = 1p;
     361                        }
     362
     363                        // The future is completed delete it now
     364                        /* paranoid */ verify( this.ptr != 1p );
     365                        free( &this );
     366                        return true;
     367                }
     368
     369                // from the server side, mark the future as fulfilled
     370                // delete it if needed
     371                bool fulfil( future_t & this ) {
     372                        for() {
     373                                struct oneshot * expected = this.ptr;
     374                                // was this abandoned?
     375                                #if defined(__GNUC__) && __GNUC__ >= 7
     376                                        #pragma GCC diagnostic push
     377                                        #pragma GCC diagnostic ignored "-Wfree-nonheap-object"
     378                                #endif
     379                                        if( expected == 3p ) { free( &this ); return false; }
     380                                #if defined(__GNUC__) && __GNUC__ >= 7
     381                                        #pragma GCC diagnostic pop
     382                                #endif
     383
     384                                /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen
     385                                /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case.
     386
     387                                // If there is a wait context, we need to consume it and mark it as consumed after
     388                                // If there is no context then we can skip the in progress phase
     389                                struct oneshot * want = expected == 0p ? 1p : 2p;
     390                                if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     391                                        if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return false; }
     392                                        bool ret = post( *expected );
     393                                        __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
     394                                        return ret;
     395                                }
     396                        }
     397
     398                }
     399
     400                // Wait for the future to be fulfilled
     401                bool wait( future_t & this ) {
     402                        oneshot temp;
     403                        if( !setup(this, temp) ) return false;
     404
     405                        // Wait context is setup, just wait on it
     406                        bool ret = wait( temp );
     407
     408                        // Wait for the future to tru
     409                        while( this.ptr == 2p ) Pause();
     410                        // Make sure the state makes sense
     411                        // Should be fulfilled, could be in progress but it's out of date if so
     412                        // since if that is the case, the oneshot was fulfilled (unparking this thread)
     413                        // and the oneshot should not be needed any more
     414                        __attribute__((unused)) struct oneshot * was = this.ptr;
     415                        /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was );
     416
     417                        // Mark the future as fulfilled, to be consistent
     418                        // with potential calls to avail
     419                        // this.ptr = 1p;
     420                        return ret;
     421                }
    151422        }
    152423#endif
  • libcfa/src/bits/signal.hfa

    r3c64c668 r58fe85a  
    1919#include "bits/defs.hfa"
    2020
    21 extern "C" {
    2221#include <errno.h>
    2322#define __USE_GNU
     
    2625#include <stdlib.h>
    2726#include <string.h>
    28 }
    2927
    3028// Short hands for signal context information
     
    5452                        sig, handler, flags, errno, strerror( errno )
    5553                );
    56                 _exit( EXIT_FAILURE );
     54                _Exit( EXIT_FAILURE );
    5755        } // if
    5856}
  • libcfa/src/common.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed Jul 11 17:54:36 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Jul 12 08:02:18 2018
    13 // Update Count     : 5
     12// Last Modified On : Sat Aug 15 08:51:29 2020
     13// Update Count     : 14
    1414//
    1515
     
    6767
    6868static inline {
     69        char min( char t1, char t2 ) { return t1 < t2 ? t1 : t2; } // optimization
     70        intptr_t min( intptr_t t1, intptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization
     71        uintptr_t min( uintptr_t t1, uintptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization
    6972        forall( otype T | { int ?<?( T, T ); } )
    7073        T min( T t1, T t2 ) { return t1 < t2 ? t1 : t2; }
    7174
     75        char max( char t1, char t2 ) { return t1 > t2 ? t1 : t2; } // optimization
     76        intptr_t max( intptr_t t1, intptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization
     77        uintptr_t max( uintptr_t t1, uintptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization
    7278        forall( otype T | { int ?>?( T, T ); } )
    7379        T max( T t1, T t2 ) { return t1 > t2 ? t1 : t2; }
  • libcfa/src/concurrency/CtxSwitch-i386.S

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Dec 6 12:27:26 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Jul 21 22:29:25 2017
    13 // Update Count     : 1
    14 //
    15 // This  library is free  software; you  can redistribute  it and/or  modify it
    16 // under the terms of the GNU Lesser General Public License as published by the
    17 // Free Software  Foundation; either  version 2.1 of  the License, or  (at your
    18 // option) any later version.
    19 //
    20 // This library is distributed in the  hope that it will be useful, but WITHOUT
    21 // ANY  WARRANTY;  without even  the  implied  warranty  of MERCHANTABILITY  or
    22 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
    23 // for more details.
    24 //
    25 // You should  have received a  copy of the  GNU Lesser General  Public License
    26 // along  with this library.
     12// Last Modified On : Sun Sep  6 18:23:37 2020
     13// Update Count     : 5
    2714//
    2815
    29 // This context switch routine depends on the fact that the stack of a new
    30 // thread has been set up to look like the thread has saved its context in
    31 // the normal manner.
    32 //
    33 // void CtxSwitch( machine_context *from, machine_context *to );
     16// The context switch routine requires the initial the stack of a thread to
     17// look like the thread has saved its context in the normal manner.
    3418
    35 // Offsets in the context structure. This needs to be synchronized with the
    36 // high level code a little better.
     19// Offsets must synchronized with the __stack_context_t in invoke.h.
    3720
    3821#define PTR_BYTE        4
    3922#define SP_OFFSET       ( 0 * PTR_BYTE )
    4023#define FP_OFFSET       ( 1 * PTR_BYTE )
    41 #define PC_OFFSET       ( 2 * PTR_BYTE )
    4224
     25// Context switch between coroutines/tasks.
     26//   void __cfactx_switch( struct __stack_context_t * from, struct __stack_context_t * to ) ;
     27// Arguments "from" in register 4(%esp), "to" in register 20(%esp)
     28
     29        .file "CtxSwitch-i386.S"
    4330        .text
    4431        .align 2
    45         .globl __cfactx_switch
    46         .type  __cfactx_switch, @function
     32        .global __cfactx_switch
     33        .type __cfactx_switch, @function
    4734__cfactx_switch:
    4835
    4936        // Copy the "from" context argument from the stack to register eax
    50         // Return address is at 0(%esp), with parameters following
     37        // Return address is at 0(%esp), with parameters following.
    5138
    5239        movl 4(%esp),%eax
     
    6350        movl %ebp,FP_OFFSET(%eax)
    6451
    65         // Copy the "to" context argument from the stack to register eax
    66         // Having pushed three words (= 12 bytes) on the stack, the
    67         // argument is now at 8 + 12 = 20(%esp)
     52        // Copy the "to" context argument from the stack to register eax. Having
     53        // pushed 3 words (= 12 bytes) on the stack, the argument is now at
     54        // 8 + 12 = 20(%esp).
    6855
    6956        movl 20(%esp),%eax
     
    8370
    8471        ret
    85         .size  __cfactx_switch, .-__cfactx_switch
     72        .size __cfactx_switch, .-__cfactx_switch
    8673
    8774// Local Variables: //
  • libcfa/src/concurrency/CtxSwitch-x86_64.S

    r3c64c668 r58fe85a  
    77// CtxSwitch-x86_64.S --
    88//
    9 // Author           : Thierry Delisle
    10 // Created On       : Mon Nov 28 12:27:26 2016
     9// Author           : Peter A. Buhr
     10// Created On       : Mon Aug 10 08:10:26 2020
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Jul 21 22:28:11 2017
    13 // Update Count     : 1
    14 //
    15 // This  library is free  software; you  can redistribute  it and/or  modify it
    16 // under the terms of the GNU Lesser General Public License as published by the
    17 // Free Software  Foundation; either  version 2.1 of  the License, or  (at your
    18 // option) any later version.
    19 //
    20 // This library is distributed in the  hope that it will be useful, but WITHOUT
    21 // ANY  WARRANTY;  without even  the  implied  warranty  of MERCHANTABILITY  or
    22 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
    23 // for more details.
    24 //
    25 // You should  have received a  copy of the  GNU Lesser General  Public License
    26 // along  with this library.
     12// Last Modified On : Sat Oct 24 14:36:25 2020
     13// Update Count     : 10
    2714//
    2815
    29 // This context switch routine depends on the fact that the stack of a new
    30 // thread has been set up to look like the thread has saved its context in
    31 // the normal manner.
    32 //
    33 // void CtxSwitch( machine_context *from, machine_context *to );
     16// The context switch routine requires the initial the stack of a thread to
     17// look like the thread has saved its context in the normal manner.
    3418
    35 // Offsets in the context structure. This needs to be synchronized with the
    36 // high level code a little better.
     19// Offsets must synchronized with the __stack_context_t in invoke.h.
    3720
    3821#define PTR_BYTE        8
     
    4023#define FP_OFFSET       ( 1 * PTR_BYTE )
    4124
    42 //-----------------------------------------------------------------------------
    43 // Regular context switch routine which enables switching from one context to anouther
     25// Context switch between coroutines/tasks.
     26//   void __cfactx_switch( struct __stack_context_t * from, struct __stack_context_t * to ) ;
     27// Arguments "from" in register rdi, "to" in register rsi.
     28
     29        .file "CtxSwitch-x86_64.S"
    4430        .text
    4531        .align 2
    46         .globl __cfactx_switch
    47         .type  __cfactx_switch, @function
     32        .global __cfactx_switch
     33        .type __cfactx_switch, @function
    4834__cfactx_switch:
    4935
     
    7763
    7864        ret
    79         .size  __cfactx_switch, .-__cfactx_switch
     65        .size __cfactx_switch, .-__cfactx_switch
    8066
    81 //-----------------------------------------------------------------------------
    82 // Stub used to create new stacks which are ready to be context switched to
     67// Stub to create new stacks which can be context switched to
     68//   void __cfactx_invoke_stub( void );
     69
    8370        .text
    8471        .align 2
    85         .globl __cfactx_invoke_stub
    86         .type    __cfactx_invoke_stub, @function
     72        .global __cfactx_invoke_stub
     73        .type __cfactx_invoke_stub, @function
    8774__cfactx_invoke_stub:
    88         movq %rbx, %rdi
     75        movq %rbx, %rdi                                         // move main and this to first two arguments
    8976        movq %r12, %rsi
    90         jmp *%r13
    91         .size  __cfactx_invoke_stub, .-__cfactx_invoke_stub
     77        jmp *%r13                                                       // jmp to invoke
     78        .size __cfactx_invoke_stub, .-__cfactx_invoke_stub
    9279
    9380// Local Variables: //
    94 // mode: c //
     81// mode: asm //
    9582// tab-width: 4 //
    9683// End: //
  • libcfa/src/concurrency/alarm.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Fri Jun 2 11:31:25 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan  5 08:41:36 2020
    13 // Update Count     : 69
     12// Last Modified On : Wed Jun 17 16:11:35 2020
     13// Update Count     : 75
    1414//
    1515
    1616#define __cforall_thread__
    1717
    18 extern "C" {
    1918#include <errno.h>
    2019#include <stdio.h>
     20#include <unistd.h>
    2121#include <string.h>
    22 #include <unistd.h>
    2322#include <sys/time.h>
    24 }
    2523
    2624#include "alarm.hfa"
    27 #include "kernel_private.hfa"
     25#include "kernel/fwd.hfa"
    2826#include "preemption.hfa"
    2927
     
    4745//=============================================================================================
    4846
    49 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period ) with( this ) {
     47void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period) with( this ) {
    5048        this.thrd = thrd;
    5149        this.alarm = alarm;
    5250        this.period = period;
    53         next = 0;
    5451        set = false;
    55         kernel_alarm = false;
     52        type = User;
    5653}
    5754
    58 void ?{}( alarm_node_t & this, processor   * proc, Time alarm, Duration period ) with( this ) {
     55void ?{}( alarm_node_t & this, processor * proc, Time alarm, Duration period ) with( this ) {
    5956        this.proc = proc;
    6057        this.alarm = alarm;
    6158        this.period = period;
    62         next = 0;
    6359        set = false;
    64         kernel_alarm = true;
     60        type = Kernel;
     61}
     62void ?{}( alarm_node_t & this, Alarm_Callback callback, Time alarm, Duration period ) with( this ) {
     63        this.alarm = alarm;
     64        this.period = period;
     65        this.callback = callback;
     66        set = false;
     67        type = Callback;
    6568}
    6669
     
    7174}
    7275
    73 #if !defined(NDEBUG) && (defined(__CFA_DEBUG__) || defined(__CFA_VERIFY__))
    74 bool validate( alarm_list_t * this ) {
    75         alarm_node_t ** it = &this->head;
    76         while( (*it) ) {
    77                 it = &(*it)->next;
     76void insert( alarm_list_t * this, alarm_node_t * n ) {
     77        alarm_node_t * it = & (*this)`first;
     78        while( it && (n->alarm > it->alarm) ) {
     79                it = & (*it)`next;
     80        }
     81        if ( it ) {
     82                insert_before( *it, *n );
     83        } else {
     84                insert_last(*this, *n);
    7885        }
    7986
    80         return it == this->tail;
    81 }
    82 #endif
    83 
    84 static inline void insert_at( alarm_list_t * this, alarm_node_t * n, __alarm_it_t p ) {
    85         verify( !n->next );
    86         if( p == this->tail ) {
    87                 this->tail = &n->next;
    88         }
    89         else {
    90                 n->next = *p;
    91         }
    92         *p = n;
    93 
    94         verify( validate( this ) );
    95 }
    96 
    97 void insert( alarm_list_t * this, alarm_node_t * n ) {
    98         alarm_node_t ** it = &this->head;
    99         while( (*it) && (n->alarm > (*it)->alarm) ) {
    100                 it = &(*it)->next;
    101         }
    102 
    103         insert_at( this, n, it );
    104 
    105         verify( validate( this ) );
     87        verify( validate( *this ) );
    10688}
    10789
    10890alarm_node_t * pop( alarm_list_t * this ) {
    109         alarm_node_t * head = this->head;
     91        verify( validate( *this ) );
     92        alarm_node_t * head = & (*this)`first;
    11093        if( head ) {
    111                 this->head = head->next;
    112                 if( !head->next ) {
    113                         this->tail = &this->head;
    114                 }
    115                 head->next = 0p;
     94                remove(*head);
    11695        }
    117         verify( validate( this ) );
     96        verify( validate( *this ) );
    11897        return head;
    11998}
    12099
    121 static inline void remove_at( alarm_list_t * this, alarm_node_t * n, __alarm_it_t it ) {
    122         verify( it );
    123         verify( (*it) == n );
    124 
    125         (*it) = n->next;
    126         if( !n-> next ) {
    127                 this->tail = it;
    128         }
    129         n->next = 0p;
    130 
    131         verify( validate( this ) );
    132 }
    133 
    134 static inline void remove( alarm_list_t * this, alarm_node_t * n ) {
    135         alarm_node_t ** it = &this->head;
    136         while( (*it) && (*it) != n ) {
    137                 it = &(*it)->next;
    138         }
    139 
    140         verify( validate( this ) );
    141 
    142         if( *it ) { remove_at( this, n, it ); }
    143 
    144         verify( validate( this ) );
    145 }
    146 
    147100void register_self( alarm_node_t * this ) {
    148         alarm_list_t * alarms = &event_kernel->alarms;
     101        alarm_list_t & alarms = event_kernel->alarms;
    149102
    150103        disable_interrupts();
     
    152105        {
    153106                verify( validate( alarms ) );
    154                 bool first = !alarms->head;
     107                bool first = ! & alarms`first;
    155108
    156                 insert( alarms, this );
     109                insert( &alarms, this );
    157110                if( first ) {
    158                         __kernel_set_timer( alarms->head->alarm - __kernel_get_time() );
     111                        __kernel_set_timer( alarms`first.alarm - __kernel_get_time() );
    159112                }
    160113        }
     
    168121        lock( event_kernel->lock __cfaabi_dbg_ctx2 );
    169122        {
    170                 verify( validate( &event_kernel->alarms ) );
    171                 remove( &event_kernel->alarms, this );
     123                verify( validate( event_kernel->alarms ) );
     124                remove( *this );
    172125        }
    173126        unlock( event_kernel->lock );
     
    176129}
    177130
     131//=============================================================================================
     132// Utilities
     133//=============================================================================================
     134
     135void sleep( Duration duration ) {
     136        alarm_node_t node = { active_thread(), __kernel_get_time() + duration, 0`s };
     137
     138        register_self( &node );
     139        park();
     140
     141        /* paranoid */ verify( !node.set );
     142        /* paranoid */ verify( & node`next == 0p );
     143        /* paranoid */ verify( & node`prev == 0p );
     144}
     145
    178146// Local Variables: //
    179147// mode: c //
  • libcfa/src/concurrency/alarm.hfa

    r3c64c668 r58fe85a  
    2323#include "time.hfa"
    2424
     25#include "containers/list.hfa"
     26
    2527struct $thread;
    2628struct processor;
     
    3739//=============================================================================================
    3840
     41enum alarm_type{ Kernel = 0, User = 1, Callback = 2 };
     42
     43struct alarm_node_t;
     44
     45typedef void (*Alarm_Callback)(alarm_node_t & );
     46
    3947struct alarm_node_t {
    4048        Time alarm;                             // time when alarm goes off
    4149        Duration period;                        // if > 0 => period of alarm
    42         alarm_node_t * next;            // intrusive link list field
     50
     51        DLISTED_MGD_IMPL_IN(alarm_node_t)
    4352
    4453        union {
    45                 $thread * thrd; // thrd who created event
    46                 processor * proc;               // proc who created event
     54                $thread * thrd;                                 // thrd who created event
     55                processor * proc;                               // proc who created event
     56                Alarm_Callback callback;                // callback to handle event
    4757        };
    4858
    4959        bool set                :1;             // whether or not the alarm has be registered
    50         bool kernel_alarm       :1;             // true if this is not a user defined alarm
     60        enum alarm_type type;           // true if this is not a user defined alarm
    5161};
    52 
    53 typedef alarm_node_t ** __alarm_it_t;
     62DLISTED_MGD_IMPL_OUT(alarm_node_t)
    5463
    5564void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period );
    5665void ?{}( alarm_node_t & this, processor   * proc, Time alarm, Duration period );
     66void ?{}( alarm_node_t & this, Alarm_Callback callback, Time alarm, Duration period );
    5767void ^?{}( alarm_node_t & this );
    5868
    59 struct alarm_list_t {
    60         alarm_node_t * head;
    61         __alarm_it_t tail;
    62 };
    63 
    64 static inline void ?{}( alarm_list_t & this ) with( this ) {
    65         head = 0;
    66         tail = &head;
    67 }
     69typedef dlist(alarm_node_t, alarm_node_t) alarm_list_t;
    6870
    6971void insert( alarm_list_t * this, alarm_node_t * n );
  • libcfa/src/concurrency/coroutine.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Mon Nov 28 12:27:26 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 12:29:25 2020
    13 // Update Count     : 16
     12// Last Modified On : Tue Dec 15 12:06:04 2020
     13// Update Count     : 23
    1414//
    1515
     
    1818#include "coroutine.hfa"
    1919
    20 extern "C" {
    2120#include <stddef.h>
    2221#include <malloc.h>
     
    2423#include <string.h>
    2524#include <unistd.h>
    26 // use this define to make unwind.h play nice, definetely a hack
    27 #define HIDE_EXPORTS
     25#include <sys/mman.h>                                                                   // mprotect
    2826#include <unwind.h>
    29 #undef HIDE_EXPORTS
    30 #include <sys/mman.h>
    31 }
    3227
    3328#include "kernel_private.hfa"
     29#include "exception.hfa"
     30#include "math.hfa"
     31
     32#define CFA_COROUTINE_USE_MMAP 0
    3433
    3534#define __CFA_INVOKE_PRIVATE__
     
    4746
    4847//-----------------------------------------------------------------------------
     48FORALL_DATA_INSTANCE(CoroutineCancelled, (dtype coroutine_t), (coroutine_t))
     49
     50forall(dtype T)
     51void mark_exception(CoroutineCancelled(T) *) {}
     52
     53forall(dtype T)
     54void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src) {
     55        dst->virtual_table = src->virtual_table;
     56        dst->the_coroutine = src->the_coroutine;
     57        dst->the_exception = src->the_exception;
     58}
     59
     60forall(dtype T)
     61const char * msg(CoroutineCancelled(T) *) {
     62        return "CoroutineCancelled(...)";
     63}
     64
     65// This code should not be inlined. It is the error path on resume.
     66forall(dtype T | is_coroutine(T))
     67void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ) {
     68        verify( desc->cancellation );
     69        desc->state = Cancelled;
     70        exception_t * except = __cfaehm_cancellation_exception( desc->cancellation );
     71
     72        // TODO: Remove explitate vtable set once trac#186 is fixed.
     73        CoroutineCancelled(T) except;
     74        except.virtual_table = &get_exception_vtable(&except);
     75        except.the_coroutine = &cor;
     76        except.the_exception = except;
     77        throwResume except;
     78
     79        except->virtual_table->free( except );
     80        free( desc->cancellation );
     81        desc->cancellation = 0p;
     82}
     83
     84//-----------------------------------------------------------------------------
    4985// Global state variables
    5086
    5187// minimum feasible stack size in bytes
    52 #define MinStackSize 1000
     88static const size_t MinStackSize = 1000;
    5389extern size_t __page_size;                              // architecture pagesize HACK, should go in proper runtime singleton
     90extern int __map_prot;
    5491
    5592void __stack_prepare( __stack_info_t * this, size_t create_size );
     93void __stack_clean  ( __stack_info_t * this );
    5694
    5795//-----------------------------------------------------------------------------
     
    74112        bool userStack = ((intptr_t)this.storage & 0x1) != 0;
    75113        if ( ! userStack && this.storage ) {
    76                 __attribute__((may_alias)) intptr_t * istorage = (intptr_t *)&this.storage;
    77                 *istorage &= (intptr_t)-1;
    78 
    79                 void * storage = this.storage->limit;
    80                 __cfaabi_dbg_debug_do(
    81                         storage = (char*)(storage) - __page_size;
    82                         if ( mprotect( storage, __page_size, PROT_READ | PROT_WRITE ) == -1 ) {
    83                                 abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) );
    84                         }
    85                 );
    86                 __cfaabi_dbg_print_safe("Kernel : Deleting stack %p\n", storage);
    87                 free( storage );
     114                __stack_clean( &this );
    88115        }
    89116}
     
    101128void ^?{}($coroutine& this) {
    102129        if(this.state != Halted && this.state != Start && this.state != Primed) {
    103                 $coroutine * src = TL_GET( this_thread )->curr_cor;
     130                $coroutine * src = active_coroutine();
    104131                $coroutine * dst = &this;
    105132
     
    134161        assert(__page_size != 0l);
    135162        size_t size = libCeiling( storageSize, 16 ) + stack_data_size;
     163        size = ceiling(size, __page_size);
    136164
    137165        // If we are running debug, we also need to allocate a guardpage to catch stack overflows.
    138166        void * storage;
    139         __cfaabi_dbg_debug_do(
    140                 storage = memalign( __page_size, size + __page_size );
    141         );
    142         __cfaabi_dbg_no_debug_do(
    143                 storage = (void*)malloc(size);
    144         );
    145 
     167        #if CFA_COROUTINE_USE_MMAP
     168                storage = mmap(0p, size + __page_size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
     169                if(storage == ((void*)-1)) {
     170                        abort( "coroutine stack creation : internal error, mmap failure, error(%d) %s.", errno, strerror( errno ) );
     171                }
     172                if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) {
     173                        abort( "coroutine stack creation : internal error, mprotect failure, error(%d) %s.", errno, strerror( errno ) );
     174                } // if
     175                storage = (void *)(((intptr_t)storage) + __page_size);
     176        #else
     177                __cfaabi_dbg_debug_do(
     178                        storage = memalign( __page_size, size + __page_size );
     179                );
     180                __cfaabi_dbg_no_debug_do(
     181                        storage = (void*)malloc(size);
     182                );
     183
     184                __cfaabi_dbg_debug_do(
     185                        if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) {
     186                                abort( "__stack_alloc : internal error, mprotect failure, error(%d) %s.", (int)errno, strerror( (int)errno ) );
     187                        }
     188                        storage = (void *)(((intptr_t)storage) + __page_size);
     189                );
     190        #endif
    146191        __cfaabi_dbg_print_safe("Kernel : Created stack %p of size %zu\n", storage, size);
    147         __cfaabi_dbg_debug_do(
    148                 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) {
    149                         abort( "__stack_alloc : internal error, mprotect failure, error(%d) %s.", (int)errno, strerror( (int)errno ) );
    150                 }
    151                 storage = (void *)(((intptr_t)storage) + __page_size);
    152         );
    153192
    154193        verify( ((intptr_t)storage & (libAlign() - 1)) == 0ul );
    155194        return [storage, size];
     195}
     196
     197void __stack_clean  ( __stack_info_t * this ) {
     198        size_t size = ((intptr_t)this->storage->base) - ((intptr_t)this->storage->limit) + sizeof(__stack_t);
     199        void * storage = this->storage->limit;
     200
     201        #if CFA_COROUTINE_USE_MMAP
     202                storage = (void *)(((intptr_t)storage) - __page_size);
     203                if(munmap(storage, size + __page_size) == -1) {
     204                        abort( "coroutine stack destruction : internal error, munmap failure, error(%d) %s.", errno, strerror( errno ) );
     205                }
     206        #else
     207                __cfaabi_dbg_debug_do(
     208                        storage = (char*)(storage) - __page_size;
     209                        if ( mprotect( storage, __page_size, __map_prot ) == -1 ) {
     210                                abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) );
     211                        }
     212                );
     213
     214                free( storage );
     215        #endif
     216        __cfaabi_dbg_print_safe("Kernel : Deleting stack %p\n", storage);
    156217}
    157218
     
    175236                size = libFloor(create_size - stack_data_size - diff, libAlign());
    176237        } // if
    177         assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %d bytes for a stack.", size, MinStackSize );
    178 
    179         this->storage = (__stack_t *)((intptr_t)storage + size);
     238        assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %zd bytes for a stack.", size, MinStackSize );
     239
     240        this->storage = (__stack_t *)((intptr_t)storage + size - sizeof(__stack_t));
    180241        this->storage->limit = storage;
    181         this->storage->base  = (void*)((intptr_t)storage + size);
     242        this->storage->base  = (void*)((intptr_t)storage + size - sizeof(__stack_t));
     243        this->storage->exception_context.top_resume = 0p;
     244        this->storage->exception_context.current_exception = 0p;
    182245        __attribute__((may_alias)) intptr_t * istorage = (intptr_t*)&this->storage;
    183246        *istorage |= userStack ? 0x1 : 0x0;
     
    205268
    206269        struct $coroutine * __cfactx_cor_finish(void) {
    207                 struct $coroutine * cor = kernelTLS.this_thread->curr_cor;
     270                struct $coroutine * cor = active_coroutine();
    208271
    209272                if(cor->state == Primed) {
    210                         suspend();
     273                        __cfactx_suspend();
    211274                }
    212275
  • libcfa/src/concurrency/coroutine.hfa

    r3c64c668 r58fe85a  
    1818#include <assert.h>
    1919#include "invoke.h"
     20#include "../exception.hfa"
     21
     22//-----------------------------------------------------------------------------
     23// Exception thrown from resume when a coroutine stack is cancelled.
     24FORALL_DATA_EXCEPTION(CoroutineCancelled, (dtype coroutine_t), (coroutine_t)) (
     25        coroutine_t * the_coroutine;
     26        exception_t * the_exception;
     27);
     28
     29forall(dtype T)
     30void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src);
     31
     32forall(dtype T)
     33const char * msg(CoroutineCancelled(T) *);
    2034
    2135//-----------------------------------------------------------------------------
     
    2337// Anything that implements this trait can be resumed.
    2438// Anything that is resumed is a coroutine.
    25 trait is_coroutine(dtype T) {
    26       void main(T & this);
    27       $coroutine * get_coroutine(T & this);
     39trait is_coroutine(dtype T | IS_RESUMPTION_EXCEPTION(CoroutineCancelled, (T))) {
     40        void main(T & this);
     41        $coroutine * get_coroutine(T & this);
    2842};
    2943
     
    4660//-----------------------------------------------------------------------------
    4761// Public coroutine API
    48 static inline void suspend(void);
    49 
    50 forall(dtype T | is_coroutine(T))
    51 static inline T & resume(T & cor);
    52 
    5362forall(dtype T | is_coroutine(T))
    5463void prime(T & cor);
    5564
    56 static inline struct $coroutine * active_coroutine() { return TL_GET( this_thread )->curr_cor; }
     65static inline struct $coroutine * active_coroutine() { return active_thread()->curr_cor; }
    5766
    5867//-----------------------------------------------------------------------------
     
    7584static inline void $ctx_switch( $coroutine * src, $coroutine * dst ) __attribute__((nonnull (1, 2))) {
    7685        // set state of current coroutine to inactive
    77         src->state = src->state == Halted ? Halted : Inactive;
     86        src->state = src->state == Halted ? Halted : Blocked;
    7887
    7988        // set new coroutine that task is executing
    80         TL_GET( this_thread )->curr_cor = dst;
     89        active_thread()->curr_cor = dst;
    8190
    8291        // context switch to specified coroutine
     
    93102}
    94103
    95 extern void __stack_prepare   ( __stack_info_t * this, size_t size /* ignored if storage already allocated */);
     104extern void __stack_prepare( __stack_info_t * this, size_t size /* ignored if storage already allocated */);
     105extern void __stack_clean  ( __stack_info_t * this );
     106
    96107
    97108// Suspend implementation inlined for performance
    98 static inline void suspend(void) {
    99         // optimization : read TLS once and reuse it
    100         // Safety note: this is preemption safe since if
    101         // preemption occurs after this line, the pointer
    102         // will also migrate which means this value will
    103         // stay in syn with the TLS
    104         $coroutine * src = TL_GET( this_thread )->curr_cor;
     109extern "C" {
     110        static inline void __cfactx_suspend(void) {
     111                // optimization : read TLS once and reuse it
     112                // Safety note: this is preemption safe since if
     113                // preemption occurs after this line, the pointer
     114                // will also migrate which means this value will
     115                // stay in syn with the TLS
     116                $coroutine * src = active_coroutine();
    105117
    106         assertf( src->last != 0,
    107                 "Attempt to suspend coroutine \"%.256s\" (%p) that has never been resumed.\n"
    108                 "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",
    109                 src->name, src );
    110         assertf( src->last->state != Halted,
    111                 "Attempt by coroutine \"%.256s\" (%p) to suspend back to terminated coroutine \"%.256s\" (%p).\n"
    112                 "Possible cause is terminated coroutine's main routine has already returned.",
    113                 src->name, src, src->last->name, src->last );
     118                assertf( src->last != 0,
     119                        "Attempt to suspend coroutine \"%.256s\" (%p) that has never been resumed.\n"
     120                        "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",
     121                        src->name, src );
     122                assertf( src->last->state != Halted,
     123                        "Attempt by coroutine \"%.256s\" (%p) to suspend back to terminated coroutine \"%.256s\" (%p).\n"
     124                        "Possible cause is terminated coroutine's main routine has already returned.",
     125                        src->name, src, src->last->name, src->last );
    114126
    115         $ctx_switch( src, src->last );
     127                $ctx_switch( src, src->last );
     128        }
    116129}
     130
     131forall(dtype T | is_coroutine(T))
     132void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc );
    117133
    118134// Resume implementation inlined for performance
     
    124140        // will also migrate which means this value will
    125141        // stay in syn with the TLS
    126         $coroutine * src = TL_GET( this_thread )->curr_cor;
     142        $coroutine * src = active_coroutine();
    127143        $coroutine * dst = get_coroutine(cor);
    128144
    129145        if( unlikely(dst->context.SP == 0p) ) {
    130                 TL_GET( this_thread )->curr_cor = dst;
    131146                __stack_prepare(&dst->stack, 65000);
    132147                __cfactx_start(main, dst, cor, __cfactx_invoke_coroutine);
    133                 TL_GET( this_thread )->curr_cor = src;
    134148        }
    135149
     
    148162        // always done for performance testing
    149163        $ctx_switch( src, dst );
     164        if ( unlikely(dst->cancellation) ) {
     165                __cfaehm_cancelled_coroutine( cor, dst );
     166        }
    150167
    151168        return cor;
     
    158175        // will also migrate which means this value will
    159176        // stay in syn with the TLS
    160         $coroutine * src = TL_GET( this_thread )->curr_cor;
     177        $coroutine * src = active_coroutine();
    161178
    162179        // not resuming self ?
  • libcfa/src/concurrency/invoke.c

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Jan 17 12:27:26 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Feb  9 16:37:42 2018
    13 // Update Count     : 5
     12// Last Modified On : Sat Oct 24 14:35:28 2020
     13// Update Count     : 32
    1414//
    1515
     
    109109
    110110        struct FakeStack {
    111             void *fixedRegisters[3];              // fixed registers ebx, edi, esi (popped on 1st uSwitch, values unimportant)
    112             void *rturn;                          // where to go on return from uSwitch
    113             void *dummyReturn;                    // fake return compiler would have pushed on call to uInvoke
    114             void *argument[3];                    // for 16-byte ABI, 16-byte alignment starts here
    115             void *padding;                        // padding to force 16-byte alignment, as "base" is 16-byte aligned
     111            void *fixedRegisters[3];                                            // fixed registers ebx, edi, esi (popped on 1st uSwitch, values unimportant)
     112            void *rturn;                                                                        // where to go on return from uSwitch
     113            void *dummyReturn;                                                          // fake return compiler would have pushed on call to uInvoke
     114            void *argument[3];                                                          // for 16-byte ABI, 16-byte alignment starts here
     115            void *padding;                                                                      // padding to force 16-byte alignment, as "base" is 16-byte aligned
    116116        };
    117117
     
    122122
    123123        fs->dummyReturn = NULL;
    124         fs->argument[0] = main;     // argument to invoke
    125         fs->argument[1] = this;     // argument to invoke
     124        fs->argument[0] = main;                                                         // argument to invoke
     125        fs->argument[1] = this;                                                         // argument to invoke
    126126        fs->rturn = invoke;
    127127
     
    129129
    130130        struct FakeStack {
    131                 void *fixedRegisters[5];            // fixed registers rbx, r12, r13, r14, r15
    132                 void *rturn;                        // where to go on return from uSwitch
    133                 void *dummyReturn;                  // NULL return address to provide proper alignment
     131                void *fixedRegisters[5];                                                // fixed registers rbx, r12, r13, r14, r15
     132                void *rturn;                                                                    // where to go on return from uSwitch
     133                void *dummyReturn;                                                              // NULL return address to provide proper alignment
    134134        };
    135135
    136136        cor->context.SP = (char *)stack->base - sizeof( struct FakeStack );
    137         cor->context.FP = NULL;         // terminate stack with NULL fp
     137        cor->context.FP = NULL;                                                         // terminate stack with NULL fp
    138138
    139139        struct FakeStack *fs = (struct FakeStack *)cor->context.SP;
     
    141141        fs->dummyReturn = NULL;
    142142        fs->rturn = __cfactx_invoke_stub;
    143         fs->fixedRegisters[0] = main;
    144         fs->fixedRegisters[1] = this;
     143        fs->fixedRegisters[0] = main;                                           // argument to invoke
     144        fs->fixedRegisters[1] = this;                                           // argument to invoke
    145145        fs->fixedRegisters[2] = invoke;
    146146
    147 #elif defined( __ARM_ARCH )
    148 #error ARM needs to be upgrade to use to parameters like X86/X64 (A.K.A. : I broke this and do not know how to fix it)
     147#elif defined( __ARM_ARCH_32 )
     148#error ARM needs to be upgrade to use two parameters like X86/X64 (A.K.A. : I broke this and do not know how to fix it)
     149        // More details about the error:
     150        // To avoid the thunk problem, I changed the invoke routine to pass the main explicitly
     151        // instead of relying on an assertion. This effectively hoists any required thunk one level
     152        // which was enough to get to global scope in most cases.
     153        // This means that __cfactx_invoke_... now takes two parameters and the FakeStack needs
     154        // to be adjusted as a consequence of that.
     155        // I don't know how to do that for ARM, hence the #error
     156
    149157        struct FakeStack {
    150                 float fpRegs[16];                       // floating point registers
    151                 void *intRegs[9];                       // integer/pointer registers
    152                 void *arg[2];                           // placeholder for this pointer
     158                float fpRegs[16];                                                               // floating point registers
     159                void * intRegs[9];                                                              // integer/pointer registers
     160                void * arg[2];                                                                  // placeholder for this pointer
    153161        };
    154162
     
    162170        fs->arg[1] = invoke;
    163171
     172#elif defined( __ARM_ARCH )
     173        struct FakeStack {
     174                void * intRegs[12];                                                             // x19-x30 integer registers
     175                double fpRegs[8];                                                               // v8-v15 floating point
     176        };
     177
     178        cor->context.SP = (char *)stack->base - sizeof( struct FakeStack );
     179        cor->context.FP = NULL;
     180
     181        struct FakeStack *fs = (struct FakeStack *)cor->context.SP;
     182
     183        fs->intRegs[0] = main;                                                          // argument to invoke x19 => x0
     184        fs->intRegs[1] = this;                                                          // argument to invoke x20 => x1
     185        fs->intRegs[2] = invoke;
     186        fs->intRegs[11] = __cfactx_invoke_stub;                         // link register x30 => ret moves to pc
    164187#else
    165188        #error uknown hardware architecture
  • libcfa/src/concurrency/invoke.h

    r3c64c668 r58fe85a  
    1717#include "bits/defs.hfa"
    1818#include "bits/locks.hfa"
     19#include "kernel/fwd.hfa"
    1920
    2021#ifdef __cforall
     
    2627#define _INVOKE_H_
    2728
    28 #ifdef __ARM_ARCH
    29         // function prototypes are only really used by these macros on ARM
    30         void disable_global_interrupts();
    31         void enable_global_interrupts();
    32 
    33         #define TL_GET( member ) ( { __typeof__( kernelTLS.member ) target; \
    34                 disable_global_interrupts(); \
    35                 target = kernelTLS.member; \
    36                 enable_global_interrupts(); \
    37                 target; } )
    38         #define TL_SET( member, value ) disable_global_interrupts(); \
    39                 kernelTLS.member = value; \
    40                 enable_global_interrupts();
    41 #else
    42         #define TL_GET( member ) kernelTLS.member
    43         #define TL_SET( member, value ) kernelTLS.member = value;
    44 #endif
    45 
    46         #ifdef __cforall
    47         extern "Cforall" {
    48                 extern __attribute__((aligned(128))) thread_local struct KernelThreadData {
    49                         struct $thread    * volatile this_thread;
    50                         struct processor      * volatile this_processor;
    51 
    52                         struct {
    53                                 volatile unsigned short disable_count;
    54                                 volatile bool enabled;
    55                                 volatile bool in_progress;
    56                         } preemption_state;
    57 
    58                         uint32_t rand_seed;
    59                 } kernelTLS __attribute__ ((tls_model ( "initial-exec" )));
    60         }
    61         #endif
     29        struct __cfaehm_try_resume_node;
     30        struct __cfaehm_base_exception_t;
     31        struct exception_context_t {
     32                struct __cfaehm_try_resume_node * top_resume;
     33                struct __cfaehm_base_exception_t * current_exception;
     34        };
    6235
    6336        struct __stack_context_t {
     
    8558                // base of stack
    8659                void * base;
     60
     61                // Information for exception handling.
     62                struct exception_context_t exception_context;
    8763        };
    8864
     
    9268        };
    9369
    94         enum coroutine_state { Halted, Start, Primed, Inactive, Active, Rerun };
    95         enum __Preemption_Reason { __NO_PREEMPTION, __ALARM_PREEMPTION, __POLL_PREEMPTION, __MANUAL_PREEMPTION };
     70        enum __Coroutine_State { Halted, Start, Primed, Blocked, Ready, Active, Cancelled, Halting };
    9671
    9772        struct $coroutine {
     
    10681
    10782                // current execution status for coroutine
    108                 enum coroutine_state state;
     83                enum __Coroutine_State state;
    10984
    11085                // first coroutine to resume this one
     
    11893
    11994        };
     95        // Wrapper for gdb
     96        struct cfathread_coroutine_t { struct $coroutine debug; };
     97
     98        static inline struct __stack_t * __get_stack( struct $coroutine * cor ) {
     99                return (struct __stack_t*)(((uintptr_t)cor->stack.storage) & ((uintptr_t)-2));
     100        }
    120101
    121102        // struct which calls the monitor is accepting
     
    150131                struct __condition_node_t * dtor_node;
    151132        };
     133        // Wrapper for gdb
     134        struct cfathread_monitor_t { struct $monitor debug; };
    152135
    153136        struct __monitor_group_t {
     
    157140                // last function that acquired monitors
    158141                fptr_t func;
     142        };
     143
     144        // Link lists fields
     145        // instrusive link field for threads
     146        struct __thread_desc_link {
     147                struct $thread * next;
     148                struct $thread * prev;
     149                volatile unsigned long long ts;
     150                int preferred;
    159151        };
    160152
     
    165157
    166158                // current execution status for coroutine
    167                 volatile int state;
    168                 enum __Preemption_Reason preempted;
     159                // Possible values are:
     160                //    - TICKET_BLOCKED (-1) thread is blocked
     161                //    - TICKET_RUNNING ( 0) thread is running
     162                //    - TICKET_UNBLOCK ( 1) thread should ignore next block
     163                volatile int ticket;
     164                enum __Coroutine_State state:8;
     165                enum __Preemption_Reason preempted:8;
    169166
    170167                //SKULLDUGGERY errno is not save in the thread data structure because returnToKernel appears to be the only function to require saving and restoring it
     168
     169                // pointer to the cluster on which the thread is running
     170                struct cluster * curr_cluster;
     171
     172                // Link lists fields
     173                // instrusive link field for threads
     174                struct __thread_desc_link link;
    171175
    172176                // coroutine body used to store context
     
    182186                struct $monitor *  self_mon_p;
    183187
    184                 // pointer to the cluster on which the thread is running
    185                 struct cluster * curr_cluster;
    186 
    187188                // monitors currently held by this thread
    188189                struct __monitor_group_t monitors;
    189190
    190                 // Link lists fields
    191                 // instrusive link field for threads
    192                 struct $thread * next;
     191                // used to put threads on user data structures
     192                struct {
     193                        struct $thread * next;
     194                        struct $thread * back;
     195                } seqable;
    193196
    194197                struct {
     
    196199                        struct $thread * prev;
    197200                } node;
    198         };
     201
     202                #if defined( __CFA_WITH_VERIFY__ )
     203                        void * canary;
     204                #endif
     205        };
     206        // Wrapper for gdb
     207        struct cfathread_thread_t { struct $thread debug; };
     208
     209        #ifdef __CFA_DEBUG__
     210                void __cfaabi_dbg_record_thrd($thread & this, bool park, const char prev_name[]);
     211        #else
     212                #define __cfaabi_dbg_record_thrd(x, y, z)
     213        #endif
    199214
    200215        #ifdef __cforall
    201216        extern "Cforall" {
     217
    202218                static inline $thread *& get_next( $thread & this ) __attribute__((const)) {
    203                         return this.next;
     219                        return this.link.next;
    204220                }
    205221
    206222                static inline [$thread *&, $thread *& ] __get( $thread & this ) __attribute__((const)) {
    207223                        return this.node.[next, prev];
     224                }
     225
     226                static inline $thread *& Back( $thread * this ) __attribute__((const)) {
     227                        return this->seqable.back;
     228                }
     229
     230                static inline $thread *& Next( $thread * this ) __attribute__((const)) {
     231                        return this->seqable.next;
     232                }
     233
     234                static inline bool listed( $thread * this ) {
     235                        return this->seqable.next != 0p;
    208236                }
    209237
  • libcfa/src/concurrency/kernel.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Jan 17 12:27:26 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 13:03:15 2020
    13 // Update Count     : 58
     12// Last Modified On : Mon Aug 31 07:08:20 2020
     13// Update Count     : 71
    1414//
    1515
    1616#define __cforall_thread__
     17// #define __CFA_DEBUG_PRINT_RUNTIME_CORE__
    1718
    1819//C Includes
    19 #include <stddef.h>
    2020#include <errno.h>
    21 #include <string.h>
    22 extern "C" {
    2321#include <stdio.h>
    24 #include <fenv.h>
    25 #include <sys/resource.h>
    2622#include <signal.h>
    2723#include <unistd.h>
    28 #include <limits.h>                                                                             // PTHREAD_STACK_MIN
    29 #include <sys/mman.h>                                                                   // mprotect
    30 }
    3124
    3225//CFA Includes
    33 #include "time.hfa"
    3426#include "kernel_private.hfa"
    3527#include "preemption.hfa"
    36 #include "startup.hfa"
    3728
    3829//Private includes
     
    4031#include "invoke.h"
    4132
     33
    4234//-----------------------------------------------------------------------------
    4335// Some assembly required
    4436#if defined( __i386 )
    45         #define CtxGet( ctx )        \
    46                 __asm__ volatile (     \
    47                         "movl %%esp,%0\n"\
    48                         "movl %%ebp,%1\n"\
    49                         : "=rm" (ctx.SP),\
    50                                 "=rm" (ctx.FP) \
    51                 )
    52 
    5337        // mxcr : SSE Status and Control bits (control bits are preserved across function calls)
    5438        // fcw  : X87 FPU control word (preserved across function calls)
     
    7256
    7357#elif defined( __x86_64 )
    74         #define CtxGet( ctx )        \
    75                 __asm__ volatile (     \
    76                         "movq %%rsp,%0\n"\
    77                         "movq %%rbp,%1\n"\
    78                         : "=rm" (ctx.SP),\
    79                                 "=rm" (ctx.FP) \
    80                 )
    81 
    8258        #define __x87_store         \
    8359                uint32_t __mxcr;      \
     
    9874                )
    9975
    100 
    101 #elif defined( __ARM_ARCH )
    102 #define CtxGet( ctx ) __asm__ ( \
    103                 "mov %0,%%sp\n"   \
    104                 "mov %1,%%r11\n"   \
    105         : "=rm" (ctx.SP), "=rm" (ctx.FP) )
     76#elif defined( __arm__ )
     77        #define __x87_store
     78        #define __x87_load
     79
     80#elif defined( __aarch64__ )
     81        #define __x87_store              \
     82                uint32_t __fpcntl[2];    \
     83                __asm__ volatile (    \
     84                        "mrs x9, FPCR\n" \
     85                        "mrs x10, FPSR\n"  \
     86                        "stp x9, x10, %0\n"  \
     87                        : "=m" (__fpcntl) : : "x9", "x10" \
     88                )
     89
     90        #define __x87_load         \
     91                __asm__ volatile (    \
     92                        "ldp x9, x10, %0\n"  \
     93                        "msr FPSR, x10\n"  \
     94                        "msr FPCR, x9\n" \
     95                : "=m" (__fpcntl) : : "x9", "x10" \
     96                )
     97
    10698#else
    107         #error unknown hardware architecture
     99        #error unsupported hardware architecture
    108100#endif
    109101
     102extern $thread * mainThread;
     103extern processor * mainProcessor;
     104
    110105//-----------------------------------------------------------------------------
    111 //Start and stop routine for the kernel, declared first to make sure they run first
    112 static void __kernel_startup (void) __attribute__(( constructor( STARTUP_PRIORITY_KERNEL ) ));
    113 static void __kernel_shutdown(void) __attribute__(( destructor ( STARTUP_PRIORITY_KERNEL ) ));
    114 
    115 //-----------------------------------------------------------------------------
    116 // Kernel storage
    117 KERNEL_STORAGE(cluster,         mainCluster);
    118 KERNEL_STORAGE(processor,       mainProcessor);
    119 KERNEL_STORAGE($thread, mainThread);
    120 KERNEL_STORAGE(__stack_t,       mainThreadCtx);
    121 
    122 cluster     * mainCluster;
    123 processor   * mainProcessor;
    124 $thread * mainThread;
    125 
    126 extern "C" {
    127         struct { __dllist_t(cluster) list; __spinlock_t lock; } __cfa_dbg_global_clusters;
    128 }
    129 
    130 size_t __page_size = 0;
    131 
    132 //-----------------------------------------------------------------------------
    133 // Global state
    134 thread_local struct KernelThreadData kernelTLS __attribute__ ((tls_model ( "initial-exec" ))) = {
    135         NULL,                                                                                           // cannot use 0p
    136         NULL,
    137         { 1, false, false },
    138         6u //this should be seeded better but due to a bug calling rdtsc doesn't work
    139 };
    140 
    141 //-----------------------------------------------------------------------------
    142 // Struct to steal stack
    143 struct current_stack_info_t {
    144         __stack_t * storage;                                                            // pointer to stack object
    145         void * base;                                                                            // base of stack
    146         void * limit;                                                                           // stack grows towards stack limit
    147         void * context;                                                                         // address of cfa_context_t
    148 };
    149 
    150 void ?{}( current_stack_info_t & this ) {
    151         __stack_context_t ctx;
    152         CtxGet( ctx );
    153         this.base = ctx.FP;
    154 
    155         rlimit r;
    156         getrlimit( RLIMIT_STACK, &r);
    157         size_t size = r.rlim_cur;
    158 
    159         this.limit = (void *)(((intptr_t)this.base) - size);
    160         this.context = &storage_mainThreadCtx;
    161 }
    162 
    163 //-----------------------------------------------------------------------------
    164 // Main thread construction
    165 
    166 void ?{}( $coroutine & this, current_stack_info_t * info) with( this ) {
    167         stack.storage = info->storage;
    168         with(*stack.storage) {
    169                 limit     = info->limit;
    170                 base      = info->base;
    171         }
    172         __attribute__((may_alias)) intptr_t * istorage = (intptr_t*) &stack.storage;
    173         *istorage |= 0x1;
    174         name = "Main Thread";
    175         state = Start;
    176         starter = 0p;
    177         last = 0p;
    178         cancellation = 0p;
    179 }
    180 
    181 void ?{}( $thread & this, current_stack_info_t * info) with( this ) {
    182         state = Start;
    183         self_cor{ info };
    184         curr_cor = &self_cor;
    185         curr_cluster = mainCluster;
    186         self_mon.owner = &this;
    187         self_mon.recursion = 1;
    188         self_mon_p = &self_mon;
    189         next = 0p;
    190 
    191         node.next = 0p;
    192         node.prev = 0p;
    193         doregister(curr_cluster, this);
    194 
    195         monitors{ &self_mon_p, 1, (fptr_t)0 };
    196 }
    197 
    198 //-----------------------------------------------------------------------------
    199 // Processor coroutine
    200 void ?{}(processorCtx_t & this) {
    201 
    202 }
    203 
    204 // Construct the processor context of non-main processors
    205 static void ?{}(processorCtx_t & this, processor * proc, current_stack_info_t * info) {
    206         (this.__cor){ info };
    207         this.proc = proc;
    208 }
    209 
    210 static void * __invoke_processor(void * arg);
    211 
    212 void ?{}(processor & this, const char name[], cluster & cltr) with( this ) {
    213         this.name = name;
    214         this.cltr = &cltr;
    215         terminated{ 0 };
    216         destroyer = 0p;
    217         do_terminate = false;
    218         preemption_alarm = 0p;
    219         pending_preemption = false;
    220         runner.proc = &this;
    221 
    222         idleLock{};
    223 
    224         __cfaabi_dbg_print_safe("Kernel : Starting core %p\n", &this);
    225 
    226         this.stack = __create_pthread( &this.kernel_thread, __invoke_processor, (void *)&this );
    227 
    228         __cfaabi_dbg_print_safe("Kernel : core %p started\n", &this);
    229 }
    230 
    231 void ^?{}(processor & this) with( this ){
    232         if( ! __atomic_load_n(&do_terminate, __ATOMIC_ACQUIRE) ) {
    233                 __cfaabi_dbg_print_safe("Kernel : core %p signaling termination\n", &this);
    234 
    235                 __atomic_store_n(&do_terminate, true, __ATOMIC_RELAXED);
    236                 wake( &this );
    237 
    238                 P( terminated );
    239                 verify( kernelTLS.this_processor != &this);
    240         }
    241 
    242         pthread_join( kernel_thread, 0p );
    243         free( this.stack );
    244 }
    245 
    246 void ?{}(cluster & this, const char name[], Duration preemption_rate) with( this ) {
    247         this.name = name;
    248         this.preemption_rate = preemption_rate;
    249         ready_queue{};
    250         ready_queue_lock{};
    251 
    252         procs{ __get };
    253         idles{ __get };
    254         threads{ __get };
    255 
    256         doregister(this);
    257 }
    258 
    259 void ^?{}(cluster & this) {
    260         unregister(this);
    261 }
     106// Kernel Scheduling logic
     107static $thread * __next_thread(cluster * this);
     108static $thread * __next_thread_slow(cluster * this);
     109static void __run_thread(processor * this, $thread * dst);
     110static void __wake_one(cluster * cltr);
     111
     112static void push  (__cluster_idles & idles, processor & proc);
     113static void remove(__cluster_idles & idles, processor & proc);
     114static [unsigned idle, unsigned total, * processor] query( & __cluster_idles idles );
     115
    262116
    263117//=============================================================================================
    264118// Kernel Scheduling logic
    265119//=============================================================================================
    266 static $thread * __next_thread(cluster * this);
    267 static void __run_thread(processor * this, $thread * dst);
    268 static void __halt(processor * this);
    269 
    270120//Main of the processor contexts
    271121void main(processorCtx_t & runner) {
    272122        // Because of a bug, we couldn't initialized the seed on construction
    273123        // Do it here
    274         kernelTLS.rand_seed ^= rdtscl();
     124        __cfaabi_tls.rand_seed ^= rdtscl();
     125        __cfaabi_tls.ready_rng.fwd_seed = 25214903917_l64u * (rdtscl() ^ (uintptr_t)&runner);
     126        __tls_rand_advance_bck();
    275127
    276128        processor * this = runner.proc;
    277129        verify(this);
    278130
    279         __cfaabi_dbg_print_safe("Kernel : core %p starting\n", this);
    280 
    281         doregister(this->cltr, this);
     131        __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this);
     132        #if !defined(__CFA_NO_STATISTICS__)
     133                if( this->print_halts ) {
     134                        __cfaabi_bits_print_safe( STDOUT_FILENO, "Processor : %d - %s (%p)\n", this->id, this->name, (void*)this);
     135                }
     136        #endif
    282137
    283138        {
     
    285140                preemption_scope scope = { this };
    286141
    287                 __cfaabi_dbg_print_safe("Kernel : core %p started\n", this);
     142                __cfadbg_print_safe(runtime_core, "Kernel : core %p started\n", this);
    288143
    289144                $thread * readyThread = 0p;
    290                 for( unsigned int spin_count = 0; ! __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST); spin_count++ ) {
     145                MAIN_LOOP:
     146                for() {
     147                        // Try to get the next thread
    291148                        readyThread = __next_thread( this->cltr );
    292149
    293                         if(readyThread) {
    294                                 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    295                                 /* paranoid */ verifyf( readyThread->state == Inactive || readyThread->state == Start || readyThread->preempted != __NO_PREEMPTION, "state : %d, preempted %d\n", readyThread->state, readyThread->preempted);
    296                                 /* paranoid */ verifyf( readyThread->next == 0p, "Expected null got %p", readyThread->next );
    297 
    298                                 __run_thread(this, readyThread);
    299 
    300                                 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    301 
    302                                 spin_count = 0;
    303                         } else {
    304                                 // spin(this, &spin_count);
    305                                 __halt(this);
     150                        if( !readyThread ) {
     151                                readyThread = __next_thread_slow( this->cltr );
    306152                        }
    307                 }
    308 
    309                 __cfaabi_dbg_print_safe("Kernel : core %p stopping\n", this);
    310         }
    311 
    312         unregister(this->cltr, this);
     153
     154                        HALT:
     155                        if( !readyThread ) {
     156                                // Don't block if we are done
     157                                if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
     158
     159                                #if !defined(__CFA_NO_STATISTICS__)
     160                                        __tls_stats()->ready.sleep.halts++;
     161                                #endif
     162
     163                                // Push self to idle stack
     164                                push(this->cltr->idles, * this);
     165
     166                                // Confirm the ready-queue is empty
     167                                readyThread = __next_thread_slow( this->cltr );
     168                                if( readyThread ) {
     169                                        // A thread was found, cancel the halt
     170                                        remove(this->cltr->idles, * this);
     171
     172                                        #if !defined(__CFA_NO_STATISTICS__)
     173                                                __tls_stats()->ready.sleep.cancels++;
     174                                        #endif
     175
     176                                        // continue the mai loop
     177                                        break HALT;
     178                                }
     179
     180                                #if !defined(__CFA_NO_STATISTICS__)
     181                                        if(this->print_halts) {
     182                                                __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->id, rdtscl());
     183                                        }
     184                                #endif
     185
     186                                wait( this->idle );
     187
     188                                #if !defined(__CFA_NO_STATISTICS__)
     189                                        if(this->print_halts) {
     190                                                __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->id, rdtscl());
     191                                        }
     192                                #endif
     193
     194                                // We were woken up, remove self from idle
     195                                remove(this->cltr->idles, * this);
     196
     197                                // DON'T just proceed, start looking again
     198                                continue MAIN_LOOP;
     199                        }
     200
     201                        /* paranoid */ verify( readyThread );
     202
     203                        // We found a thread run it
     204                        __run_thread(this, readyThread);
     205
     206                        // Are we done?
     207                        if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
     208                }
     209
     210                __cfadbg_print_safe(runtime_core, "Kernel : core %p stopping\n", this);
     211        }
    313212
    314213        V( this->terminated );
    315214
    316         __cfaabi_dbg_print_safe("Kernel : core %p terminated\n", this);
     215        if(this == mainProcessor) {
     216                // HACK : the coroutine context switch expects this_thread to be set
     217                // and it make sense for it to be set in all other cases except here
     218                // fake it
     219                __cfaabi_tls.this_thread = mainThread;
     220        }
     221
     222        __cfadbg_print_safe(runtime_core, "Kernel : core %p terminated\n", this);
    317223}
    318224
     
    324230// from the processor coroutine to the target thread
    325231static void __run_thread(processor * this, $thread * thrd_dst) {
     232        /* paranoid */ verify( ! __preemption_enabled() );
     233        /* paranoid */ verifyf( thrd_dst->state == Ready || thrd_dst->preempted != __NO_PREEMPTION, "state : %d, preempted %d\n", thrd_dst->state, thrd_dst->preempted);
     234        /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next );
     235        __builtin_prefetch( thrd_dst->context.SP );
     236
    326237        $coroutine * proc_cor = get_coroutine(this->runner);
    327 
    328         // Update global state
    329         kernelTLS.this_thread = thrd_dst;
    330238
    331239        // set state of processor coroutine to inactive
    332240        verify(proc_cor->state == Active);
    333         proc_cor->state = Inactive;
     241        proc_cor->state = Blocked;
    334242
    335243        // Actually run the thread
    336244        RUNNING:  while(true) {
    337                 if(unlikely(thrd_dst->preempted)) {
    338                         thrd_dst->preempted = __NO_PREEMPTION;
    339                         verify(thrd_dst->state == Active || thrd_dst->state == Rerun);
    340                 } else {
    341                         verify(thrd_dst->state == Start || thrd_dst->state == Primed || thrd_dst->state == Inactive);
    342                         thrd_dst->state = Active;
    343                 }
    344 
    345                 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
     245                thrd_dst->preempted = __NO_PREEMPTION;
     246                thrd_dst->state = Active;
     247
     248                // Update global state
     249                kernelTLS().this_thread = thrd_dst;
     250
     251                /* paranoid */ verify( ! __preemption_enabled() );
     252                /* paranoid */ verify( kernelTLS().this_thread == thrd_dst );
     253                /* paranoid */ verify( thrd_dst->curr_cluster == this->cltr );
     254                /* paranoid */ verify( thrd_dst->context.SP );
     255                /* paranoid */ verify( thrd_dst->state != Halted );
     256                /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) < ((uintptr_t)__get_stack(thrd_dst->curr_cor)->base ) || thrd_dst->curr_cor == proc_cor, "ERROR : Destination $thread %p has been corrupted.\n StackPointer too small.\n", thrd_dst ); // add escape condition if we are setting up the processor
     257                /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) > ((uintptr_t)__get_stack(thrd_dst->curr_cor)->limit) || thrd_dst->curr_cor == proc_cor, "ERROR : Destination $thread %p has been corrupted.\n StackPointer too large.\n", thrd_dst ); // add escape condition if we are setting up the processor
     258                /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_dst->canary );
     259
     260
    346261
    347262                // set context switch to the thread that the processor is executing
    348                 verify( thrd_dst->context.SP );
    349263                __cfactx_switch( &proc_cor->context, &thrd_dst->context );
    350264                // when __cfactx_switch returns we are back in the processor coroutine
    351265
    352                 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    353 
     266                /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_dst->canary );
     267                /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) > ((uintptr_t)__get_stack(thrd_dst->curr_cor)->limit), "ERROR : Destination $thread %p has been corrupted.\n StackPointer too large.\n", thrd_dst );
     268                /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) < ((uintptr_t)__get_stack(thrd_dst->curr_cor)->base ), "ERROR : Destination $thread %p has been corrupted.\n StackPointer too small.\n", thrd_dst );
     269                /* paranoid */ verify( thrd_dst->context.SP );
     270                /* paranoid */ verify( thrd_dst->curr_cluster == this->cltr );
     271                /* paranoid */ verify( kernelTLS().this_thread == thrd_dst );
     272                /* paranoid */ verify( ! __preemption_enabled() );
     273
     274                // Reset global state
     275                kernelTLS().this_thread = 0p;
    354276
    355277                // We just finished running a thread, there are a few things that could have happened.
    356278                // 1 - Regular case : the thread has blocked and now one has scheduled it yet.
    357279                // 2 - Racy case    : the thread has blocked but someone has already tried to schedule it.
    358                 // 3 - Polite Racy case : the thread has blocked, someone has already tried to schedule it, but the thread is nice and wants to go through the ready-queue any way
    359280                // 4 - Preempted
    360281                // In case 1, we may have won a race so we can't write to the state again.
    361282                // In case 2, we lost the race so we now own the thread.
    362                 // In case 3, we lost the race but can just reschedule the thread.
    363283
    364284                if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) {
     
    368288                }
    369289
     290                if(unlikely(thrd_dst->state == Halting)) {
     291                        // The thread has halted, it should never be scheduled/run again
     292                        // finish the thread
     293                        __thread_finish( thrd_dst );
     294                        break RUNNING;
     295                }
     296
     297                /* paranoid */ verify( thrd_dst->state == Active );
     298                thrd_dst->state = Blocked;
     299
    370300                // set state of processor coroutine to active and the thread to inactive
    371                 static_assert(sizeof(thrd_dst->state) == sizeof(int));
    372                 enum coroutine_state old_state = __atomic_exchange_n(&thrd_dst->state, Inactive, __ATOMIC_SEQ_CST);
    373                 switch(old_state) {
    374                         case Halted:
    375                                 // The thread has halted, it should never be scheduled/run again, leave it back to Halted and move on
    376                                 thrd_dst->state = Halted;
    377 
    378                                 // We may need to wake someone up here since
    379                                 unpark( this->destroyer );
    380                                 this->destroyer = 0p;
    381                                 break RUNNING;
    382                         case Active:
     301                int old_ticket = __atomic_fetch_sub(&thrd_dst->ticket, 1, __ATOMIC_SEQ_CST);
     302                switch(old_ticket) {
     303                        case TICKET_RUNNING:
    383304                                // This is case 1, the regular case, nothing more is needed
    384305                                break RUNNING;
    385                         case Rerun:
     306                        case TICKET_UNBLOCK:
    386307                                // This is case 2, the racy case, someone tried to run this thread before it finished blocking
    387308                                // In this case, just run it again.
     
    389310                        default:
    390311                                // This makes no sense, something is wrong abort
    391                                 abort("Finished running a thread that was Inactive/Start/Primed %d\n", old_state);
     312                                abort();
    392313                }
    393314        }
     
    395316        // Just before returning to the processor, set the processor coroutine to active
    396317        proc_cor->state = Active;
     318
     319        /* paranoid */ verify( ! __preemption_enabled() );
    397320}
    398321
    399322// KERNEL_ONLY
    400323void returnToKernel() {
    401         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    402         $coroutine * proc_cor = get_coroutine(kernelTLS.this_processor->runner);
    403         $thread * thrd_src = kernelTLS.this_thread;
     324        /* paranoid */ verify( ! __preemption_enabled() );
     325        $coroutine * proc_cor = get_coroutine(kernelTLS().this_processor->runner);
     326        $thread * thrd_src = kernelTLS().this_thread;
     327
     328        #if !defined(__CFA_NO_STATISTICS__)
     329                struct processor * last_proc = kernelTLS().this_processor;
     330        #endif
    404331
    405332        // Run the thread on this processor
     
    409336                        __x87_store;
    410337                #endif
    411                 verify( proc_cor->context.SP );
     338                /* paranoid */ verify( proc_cor->context.SP );
     339                /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_src->canary );
    412340                __cfactx_switch( &thrd_src->context, &proc_cor->context );
     341                /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_src->canary );
    413342                #if defined( __i386 ) || defined( __x86_64 )
    414343                        __x87_load;
     
    417346        }
    418347
    419         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    420 }
    421 
    422 // KERNEL_ONLY
    423 // Context invoker for processors
    424 // This is the entry point for processors (kernel threads)
    425 // It effectively constructs a coroutine by stealing the pthread stack
    426 static void * __invoke_processor(void * arg) {
    427         processor * proc = (processor *) arg;
    428         kernelTLS.this_processor = proc;
    429         kernelTLS.this_thread    = 0p;
    430         kernelTLS.preemption_state.[enabled, disable_count] = [false, 1];
    431         // SKULLDUGGERY: We want to create a context for the processor coroutine
    432         // which is needed for the 2-step context switch. However, there is no reason
    433         // to waste the perfectly valid stack create by pthread.
    434         current_stack_info_t info;
    435         __stack_t ctx;
    436         info.storage = &ctx;
    437         (proc->runner){ proc, &info };
    438 
    439         __cfaabi_dbg_print_safe("Coroutine : created stack %p\n", get_coroutine(proc->runner)->stack.storage);
    440 
    441         //Set global state
    442         kernelTLS.this_thread = 0p;
    443 
    444         //We now have a proper context from which to schedule threads
    445         __cfaabi_dbg_print_safe("Kernel : core %p created (%p, %p)\n", proc, &proc->runner, &ctx);
    446 
    447         // SKULLDUGGERY: Since the coroutine doesn't have its own stack, we can't
    448         // resume it to start it like it normally would, it will just context switch
    449         // back to here. Instead directly call the main since we already are on the
    450         // appropriate stack.
    451         get_coroutine(proc->runner)->state = Active;
    452         main( proc->runner );
    453         get_coroutine(proc->runner)->state = Halted;
    454 
    455         // Main routine of the core returned, the core is now fully terminated
    456         __cfaabi_dbg_print_safe("Kernel : core %p main ended (%p)\n", proc, &proc->runner);
    457 
    458         return 0p;
    459 }
    460 
    461 static void Abort( int ret, const char func[] ) {
    462         if ( ret ) {                                                                            // pthread routines return errno values
    463                 abort( "%s : internal error, error(%d) %s.", func, ret, strerror( ret ) );
    464         } // if
    465 } // Abort
    466 
    467 void * __create_pthread( pthread_t * pthread, void * (*start)(void *), void * arg ) {
    468         pthread_attr_t attr;
    469 
    470         Abort( pthread_attr_init( &attr ), "pthread_attr_init" ); // initialize attribute
    471 
    472         size_t stacksize;
    473         // default stack size, normally defined by shell limit
    474         Abort( pthread_attr_getstacksize( &attr, &stacksize ), "pthread_attr_getstacksize" );
    475         assert( stacksize >= PTHREAD_STACK_MIN );
    476 
    477         void * stack;
    478         __cfaabi_dbg_debug_do(
    479                 stack = memalign( __page_size, stacksize + __page_size );
    480                 // pthread has no mechanism to create the guard page in user supplied stack.
    481                 if ( mprotect( stack, __page_size, PROT_NONE ) == -1 ) {
    482                         abort( "mprotect : internal error, mprotect failure, error(%d) %s.", errno, strerror( errno ) );
    483                 } // if
    484         );
    485         __cfaabi_dbg_no_debug_do(
    486                 stack = malloc( stacksize );
    487         );
    488 
    489         Abort( pthread_attr_setstack( &attr, stack, stacksize ), "pthread_attr_setstack" );
    490 
    491         Abort( pthread_create( pthread, &attr, start, arg ), "pthread_create" );
    492         return stack;
    493 }
    494 
    495 // KERNEL_ONLY
    496 static void __kernel_first_resume( processor * this ) {
    497         $thread * src = mainThread;
    498         $coroutine * dst = get_coroutine(this->runner);
    499 
    500         verify( ! kernelTLS.preemption_state.enabled );
    501 
    502         kernelTLS.this_thread->curr_cor = dst;
    503         __stack_prepare( &dst->stack, 65000 );
    504         __cfactx_start(main, dst, this->runner, __cfactx_invoke_coroutine);
    505 
    506         verify( ! kernelTLS.preemption_state.enabled );
    507 
    508         dst->last = &src->self_cor;
    509         dst->starter = dst->starter ? dst->starter : &src->self_cor;
    510 
    511         // set state of current coroutine to inactive
    512         src->state = src->state == Halted ? Halted : Inactive;
    513 
    514         // context switch to specified coroutine
    515         verify( dst->context.SP );
    516         __cfactx_switch( &src->context, &dst->context );
    517         // when __cfactx_switch returns we are back in the src coroutine
    518 
    519         mainThread->curr_cor = &mainThread->self_cor;
    520 
    521         // set state of new coroutine to active
    522         src->state = Active;
    523 
    524         verify( ! kernelTLS.preemption_state.enabled );
    525 }
    526 
    527 // KERNEL_ONLY
    528 static void __kernel_last_resume( processor * this ) {
    529         $coroutine * src = &mainThread->self_cor;
    530         $coroutine * dst = get_coroutine(this->runner);
    531 
    532         verify( ! kernelTLS.preemption_state.enabled );
    533         verify( dst->starter == src );
    534         verify( dst->context.SP );
    535 
    536         // context switch to the processor
    537         __cfactx_switch( &src->context, &dst->context );
     348        #if !defined(__CFA_NO_STATISTICS__)
     349                if(last_proc != kernelTLS().this_processor) {
     350                        __tls_stats()->ready.threads.migration++;
     351                }
     352        #endif
     353
     354        /* paranoid */ verify( ! __preemption_enabled() );
     355        /* paranoid */ verifyf( ((uintptr_t)thrd_src->context.SP) < ((uintptr_t)__get_stack(thrd_src->curr_cor)->base ), "ERROR : Returning $thread %p has been corrupted.\n StackPointer too small.\n", thrd_src );
     356        /* paranoid */ verifyf( ((uintptr_t)thrd_src->context.SP) > ((uintptr_t)__get_stack(thrd_src->curr_cor)->limit), "ERROR : Returning $thread %p has been corrupted.\n StackPointer too large.\n", thrd_src );
    538357}
    539358
     
    541360// Scheduler routines
    542361// KERNEL ONLY
    543 void __schedule_thread( $thread * thrd ) with( *thrd->curr_cluster ) {
    544         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
     362void __schedule_thread( $thread * thrd ) {
     363        /* paranoid */ verify( ! __preemption_enabled() );
     364        /* paranoid */ verify( kernelTLS().this_proc_id );
     365        /* paranoid */ verify( thrd );
     366        /* paranoid */ verify( thrd->state != Halted );
     367        /* paranoid */ verify( thrd->curr_cluster );
    545368        /* paranoid */ #if defined( __CFA_WITH_VERIFY__ )
    546         /* paranoid */ if( thrd->state == Inactive || thrd->state == Start ) assertf( thrd->preempted == __NO_PREEMPTION,
    547                           "Error inactive thread marked as preempted, state %d, preemption %d\n", thrd->state, thrd->preempted );
    548         /* paranoid */ if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active || thrd->state == Rerun,
    549                           "Error preempted thread marked as not currently running, state %d, preemption %d\n", thrd->state, thrd->preempted );
     369        /* paranoid */  if( thrd->state == Blocked || thrd->state == Start ) assertf( thrd->preempted == __NO_PREEMPTION,
     370                                        "Error inactive thread marked as preempted, state %d, preemption %d\n", thrd->state, thrd->preempted );
     371        /* paranoid */  if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active,
     372                                        "Error preempted thread marked as not currently running, state %d, preemption %d\n", thrd->state, thrd->preempted );
    550373        /* paranoid */ #endif
    551         /* paranoid */ verifyf( thrd->next == 0p, "Expected null got %p", thrd->next );
    552 
    553         lock  ( ready_queue_lock __cfaabi_dbg_ctx2 );
    554         bool was_empty = !(ready_queue != 0);
    555         append( ready_queue, thrd );
    556         unlock( ready_queue_lock );
    557 
    558         if(was_empty) {
    559                 lock      (proc_list_lock __cfaabi_dbg_ctx2);
    560                 if(idles) {
    561                         wake_fast(idles.head);
    562                 }
    563                 unlock    (proc_list_lock);
    564         }
    565         else if( struct processor * idle = idles.head ) {
    566                 wake_fast(idle);
    567         }
    568 
    569         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
     374        /* paranoid */ verifyf( thrd->link.next == 0p, "Expected null got %p", thrd->link.next );
     375        /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd->canary );
     376
     377
     378        if (thrd->preempted == __NO_PREEMPTION) thrd->state = Ready;
     379
     380        ready_schedule_lock();
     381                // Dereference the thread now because once we push it, there is not guaranteed it's still valid.
     382                struct cluster * cl = thrd->curr_cluster;
     383
     384                // push the thread to the cluster ready-queue
     385                push( cl, thrd );
     386
     387                // variable thrd is no longer safe to use
     388
     389                // wake the cluster using the save variable.
     390                __wake_one( cl );
     391        ready_schedule_unlock();
     392
     393        /* paranoid */ verify( ! __preemption_enabled() );
    570394}
    571395
    572396// KERNEL ONLY
    573 static $thread * __next_thread(cluster * this) with( *this ) {
    574         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    575 
    576         lock( ready_queue_lock __cfaabi_dbg_ctx2 );
    577         $thread * head = pop_head( ready_queue );
    578         unlock( ready_queue_lock );
    579 
    580         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    581         return head;
     397static inline $thread * __next_thread(cluster * this) with( *this ) {
     398        /* paranoid */ verify( ! __preemption_enabled() );
     399        /* paranoid */ verify( kernelTLS().this_proc_id );
     400
     401        ready_schedule_lock();
     402                $thread * thrd = pop( this );
     403        ready_schedule_unlock();
     404
     405        /* paranoid */ verify( kernelTLS().this_proc_id );
     406        /* paranoid */ verify( ! __preemption_enabled() );
     407        return thrd;
     408}
     409
     410// KERNEL ONLY
     411static inline $thread * __next_thread_slow(cluster * this) with( *this ) {
     412        /* paranoid */ verify( ! __preemption_enabled() );
     413        /* paranoid */ verify( kernelTLS().this_proc_id );
     414
     415        ready_schedule_lock();
     416                $thread * thrd = pop_slow( this );
     417        ready_schedule_unlock();
     418
     419        /* paranoid */ verify( kernelTLS().this_proc_id );
     420        /* paranoid */ verify( ! __preemption_enabled() );
     421        return thrd;
    582422}
    583423
     
    585425        if( !thrd ) return;
    586426
    587         disable_interrupts();
    588         static_assert(sizeof(thrd->state) == sizeof(int));
    589         enum coroutine_state old_state = __atomic_exchange_n(&thrd->state, Rerun, __ATOMIC_SEQ_CST);
    590         switch(old_state) {
    591                 case Active:
     427        int old_ticket = __atomic_fetch_add(&thrd->ticket, 1, __ATOMIC_SEQ_CST);
     428        switch(old_ticket) {
     429                case TICKET_RUNNING:
    592430                        // Wake won the race, the thread will reschedule/rerun itself
    593431                        break;
    594                 case Inactive:
     432                case TICKET_BLOCKED:
    595433                        /* paranoid */ verify( ! thrd->preempted != __NO_PREEMPTION );
    596 
    597                         // Wake lost the race,
    598                         thrd->state = Inactive;
    599                         __schedule_thread( thrd );
     434                        /* paranoid */ verify( thrd->state == Blocked );
     435
     436                        {
     437                                /* paranoid */ verify( publicTLS_get(this_proc_id) );
     438                                bool full = publicTLS_get(this_proc_id)->full_proc;
     439                                if(full) disable_interrupts();
     440
     441                                /* paranoid */ verify( ! __preemption_enabled() );
     442
     443                                // Wake lost the race,
     444                                __schedule_thread( thrd );
     445
     446                                /* paranoid */ verify( ! __preemption_enabled() );
     447
     448                                if(full) enable_interrupts( __cfaabi_dbg_ctx );
     449                                /* paranoid */ verify( publicTLS_get(this_proc_id) );
     450                        }
     451
    600452                        break;
    601                 case Rerun:
    602                         abort("More than one thread attempted to schedule thread %p\n", thrd);
    603                         break;
    604                 case Halted:
    605                 case Start:
    606                 case Primed:
    607453                default:
    608454                        // This makes no sense, something is wrong abort
    609                         abort();
    610         }
     455                        abort("Thread %p (%s) has mismatch park/unpark\n", thrd, thrd->self_cor.name);
     456        }
     457}
     458
     459void park( void ) {
     460        /* paranoid */ verify( __preemption_enabled() );
     461        disable_interrupts();
     462        /* paranoid */ verify( ! __preemption_enabled() );
     463        /* paranoid */ verify( kernelTLS().this_thread->preempted == __NO_PREEMPTION );
     464
     465        returnToKernel();
     466
     467        /* paranoid */ verify( ! __preemption_enabled() );
    611468        enable_interrupts( __cfaabi_dbg_ctx );
    612 }
    613 
    614 void park( void ) {
    615         /* paranoid */ verify( kernelTLS.preemption_state.enabled );
    616         disable_interrupts();
    617         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    618         /* paranoid */ verify( kernelTLS.this_thread->preempted == __NO_PREEMPTION );
    619 
    620         returnToKernel();
    621 
    622         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    623         enable_interrupts( __cfaabi_dbg_ctx );
    624         /* paranoid */ verify( kernelTLS.preemption_state.enabled );
    625 
    626 }
    627 
    628 // KERNEL ONLY
    629 void __leave_thread() {
    630         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    631         returnToKernel();
    632         abort();
     469        /* paranoid */ verify( __preemption_enabled() );
     470
     471}
     472
     473extern "C" {
     474        // Leave the thread monitor
     475        // last routine called by a thread.
     476        // Should never return
     477        void __cfactx_thrd_leave() {
     478                $thread * thrd = active_thread();
     479                $monitor * this = &thrd->self_mon;
     480
     481                // Lock the monitor now
     482                lock( this->lock __cfaabi_dbg_ctx2 );
     483
     484                disable_interrupts();
     485
     486                /* paranoid */ verify( ! __preemption_enabled() );
     487                /* paranoid */ verify( thrd->state == Active );
     488                /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd->canary );
     489                /* paranoid */ verify( kernelTLS().this_thread == thrd );
     490                /* paranoid */ verify( thrd->context.SP );
     491                /* paranoid */ verifyf( ((uintptr_t)thrd->context.SP) > ((uintptr_t)__get_stack(thrd->curr_cor)->limit), "ERROR : $thread %p has been corrupted.\n StackPointer too large.\n", thrd );
     492                /* paranoid */ verifyf( ((uintptr_t)thrd->context.SP) < ((uintptr_t)__get_stack(thrd->curr_cor)->base ), "ERROR : $thread %p has been corrupted.\n StackPointer too small.\n", thrd );
     493
     494                thrd->state = Halting;
     495                if( TICKET_RUNNING != thrd->ticket ) { abort( "Thread terminated with pending unpark" ); }
     496                if( thrd != this->owner ) { abort( "Thread internal monitor has incorrect owner" ); }
     497                if( this->recursion != 1) { abort( "Thread internal monitor has unbalanced recursion" ); }
     498
     499                // Leave the thread
     500                returnToKernel();
     501
     502                // Control flow should never reach here!
     503                abort();
     504        }
    633505}
    634506
    635507// KERNEL ONLY
    636508bool force_yield( __Preemption_Reason reason ) {
    637         /* paranoid */ verify( kernelTLS.preemption_state.enabled );
     509        /* paranoid */ verify( __preemption_enabled() );
    638510        disable_interrupts();
    639         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    640 
    641         $thread * thrd = kernelTLS.this_thread;
    642         /* paranoid */ verify(thrd->state == Active || thrd->state == Rerun);
     511        /* paranoid */ verify( ! __preemption_enabled() );
     512
     513        $thread * thrd = kernelTLS().this_thread;
     514        /* paranoid */ verify(thrd->state == Active);
    643515
    644516        // SKULLDUGGERY: It is possible that we are preempting this thread just before
     
    647519        // If that is the case, abandon the preemption.
    648520        bool preempted = false;
    649         if(thrd->next == 0p) {
     521        if(thrd->link.next == 0p) {
    650522                preempted = true;
    651523                thrd->preempted = reason;
     
    653525        }
    654526
    655         /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
     527        /* paranoid */ verify( ! __preemption_enabled() );
    656528        enable_interrupts_noPoll();
    657         /* paranoid */ verify( kernelTLS.preemption_state.enabled );
     529        /* paranoid */ verify( __preemption_enabled() );
    658530
    659531        return preempted;
     
    661533
    662534//=============================================================================================
    663 // Kernel Setup logic
     535// Kernel Idle Sleep
    664536//=============================================================================================
    665 //-----------------------------------------------------------------------------
    666 // Kernel boot procedures
    667 static void __kernel_startup(void) {
    668         verify( ! kernelTLS.preemption_state.enabled );
    669         __cfaabi_dbg_print_safe("Kernel : Starting\n");
    670 
    671         __page_size = sysconf( _SC_PAGESIZE );
    672 
    673         __cfa_dbg_global_clusters.list{ __get };
    674         __cfa_dbg_global_clusters.lock{};
    675 
    676         // Initialize the main cluster
    677         mainCluster = (cluster *)&storage_mainCluster;
    678         (*mainCluster){"Main Cluster"};
    679 
    680         __cfaabi_dbg_print_safe("Kernel : Main cluster ready\n");
    681 
    682         // Start by initializing the main thread
    683         // SKULLDUGGERY: the mainThread steals the process main thread
    684         // which will then be scheduled by the mainProcessor normally
    685         mainThread = ($thread *)&storage_mainThread;
    686         current_stack_info_t info;
    687         info.storage = (__stack_t*)&storage_mainThreadCtx;
    688         (*mainThread){ &info };
    689 
    690         __cfaabi_dbg_print_safe("Kernel : Main thread ready\n");
    691 
    692 
    693 
    694         // Construct the processor context of the main processor
    695         void ?{}(processorCtx_t & this, processor * proc) {
    696                 (this.__cor){ "Processor" };
    697                 this.__cor.starter = 0p;
    698                 this.proc = proc;
    699         }
    700 
    701         void ?{}(processor & this) with( this ) {
    702                 name = "Main Processor";
    703                 cltr = mainCluster;
    704                 terminated{ 0 };
    705                 do_terminate = false;
    706                 preemption_alarm = 0p;
    707                 pending_preemption = false;
    708                 kernel_thread = pthread_self();
    709 
    710                 runner{ &this };
    711                 __cfaabi_dbg_print_safe("Kernel : constructed main processor context %p\n", &runner);
    712         }
    713 
    714         // Initialize the main processor and the main processor ctx
    715         // (the coroutine that contains the processing control flow)
    716         mainProcessor = (processor *)&storage_mainProcessor;
    717         (*mainProcessor){};
    718 
    719         //initialize the global state variables
    720         kernelTLS.this_processor = mainProcessor;
    721         kernelTLS.this_thread    = mainThread;
    722 
    723         // Enable preemption
    724         kernel_start_preemption();
    725 
    726         // Add the main thread to the ready queue
    727         // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread
    728         __schedule_thread(mainThread);
    729 
    730         // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX
    731         // context. Hence, the main thread does not begin through __cfactx_invoke_thread, like all other threads. The trick here is that
    732         // mainThread is on the ready queue when this call is made.
    733         __kernel_first_resume( kernelTLS.this_processor );
    734 
    735 
    736 
    737         // THE SYSTEM IS NOW COMPLETELY RUNNING
    738         __cfaabi_dbg_print_safe("Kernel : Started\n--------------------------------------------------\n\n");
    739 
    740         verify( ! kernelTLS.preemption_state.enabled );
     537// Wake a thread from the front if there are any
     538static void __wake_one(cluster * this) {
     539        /* paranoid */ verify( ! __preemption_enabled() );
     540        /* paranoid */ verify( ready_schedule_islocked() );
     541
     542        // Check if there is a sleeping processor
     543        processor * p;
     544        unsigned idle;
     545        unsigned total;
     546        [idle, total, p] = query(this->idles);
     547
     548        // If no one is sleeping, we are done
     549        if( idle == 0 ) return;
     550
     551        // We found a processor, wake it up
     552        post( p->idle );
     553
     554        #if !defined(__CFA_NO_STATISTICS__)
     555                __tls_stats()->ready.sleep.wakes++;
     556        #endif
     557
     558        /* paranoid */ verify( ready_schedule_islocked() );
     559        /* paranoid */ verify( ! __preemption_enabled() );
     560
     561        return;
     562}
     563
     564// Unconditionnaly wake a thread
     565void __wake_proc(processor * this) {
     566        __cfadbg_print_safe(runtime_core, "Kernel : waking Processor %p\n", this);
     567
     568        disable_interrupts();
     569                /* paranoid */ verify( ! __preemption_enabled() );
     570                post( this->idle );
    741571        enable_interrupts( __cfaabi_dbg_ctx );
    742         verify( TL_GET( preemption_state.enabled ) );
    743 }
    744 
    745 static void __kernel_shutdown(void) {
    746         __cfaabi_dbg_print_safe("\n--------------------------------------------------\nKernel : Shutting down\n");
    747 
    748         verify( TL_GET( preemption_state.enabled ) );
    749         disable_interrupts();
    750         verify( ! kernelTLS.preemption_state.enabled );
    751 
    752         // SKULLDUGGERY: Notify the mainProcessor it needs to terminates.
    753         // When its coroutine terminates, it return control to the mainThread
    754         // which is currently here
    755         __atomic_store_n(&mainProcessor->do_terminate, true, __ATOMIC_RELEASE);
    756         __kernel_last_resume( kernelTLS.this_processor );
    757         mainThread->self_cor.state = Halted;
    758 
    759         // THE SYSTEM IS NOW COMPLETELY STOPPED
    760 
    761         // Disable preemption
    762         kernel_stop_preemption();
    763 
    764         // Destroy the main processor and its context in reverse order of construction
    765         // These were manually constructed so we need manually destroy them
    766         ^(mainProcessor->runner){};
    767         ^(mainProcessor){};
    768 
    769         // Final step, destroy the main thread since it is no longer needed
    770         // Since we provided a stack to this taxk it will not destroy anything
    771         ^(mainThread){};
    772 
    773         ^(__cfa_dbg_global_clusters.list){};
    774         ^(__cfa_dbg_global_clusters.lock){};
    775 
    776         __cfaabi_dbg_print_safe("Kernel : Shutdown complete\n");
    777 }
    778 
    779 //=============================================================================================
    780 // Kernel Quiescing
    781 //=============================================================================================
    782 static void __halt(processor * this) with( *this ) {
    783         // verify( ! __atomic_load_n(&do_terminate, __ATOMIC_SEQ_CST) );
    784 
    785         with( *cltr ) {
    786                 lock      (proc_list_lock __cfaabi_dbg_ctx2);
    787                 remove    (procs, *this);
    788                 push_front(idles, *this);
    789                 unlock    (proc_list_lock);
    790         }
    791 
    792         __cfaabi_dbg_print_safe("Kernel : Processor %p ready to sleep\n", this);
    793 
    794         wait( idleLock );
    795 
    796         __cfaabi_dbg_print_safe("Kernel : Processor %p woke up and ready to run\n", this);
    797 
    798         with( *cltr ) {
    799                 lock      (proc_list_lock __cfaabi_dbg_ctx2);
    800                 remove    (idles, *this);
    801                 push_front(procs, *this);
    802                 unlock    (proc_list_lock);
     572}
     573
     574static void push  (__cluster_idles & this, processor & proc) {
     575        /* paranoid */ verify( ! __preemption_enabled() );
     576        lock( this );
     577                this.idle++;
     578                /* paranoid */ verify( this.idle <= this.total );
     579
     580                insert_first(this.list, proc);
     581        unlock( this );
     582        /* paranoid */ verify( ! __preemption_enabled() );
     583}
     584
     585static void remove(__cluster_idles & this, processor & proc) {
     586        /* paranoid */ verify( ! __preemption_enabled() );
     587        lock( this );
     588                this.idle--;
     589                /* paranoid */ verify( this.idle >= 0 );
     590
     591                remove(proc);
     592        unlock( this );
     593        /* paranoid */ verify( ! __preemption_enabled() );
     594}
     595
     596static [unsigned idle, unsigned total, * processor] query( & __cluster_idles this ) {
     597        for() {
     598                uint64_t l = __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST);
     599                if( 1 == (l % 2) ) { Pause(); continue; }
     600                unsigned idle    = this.idle;
     601                unsigned total   = this.total;
     602                processor * proc = &this.list`first;
     603                // Compiler fence is unnecessary, but gcc-8 and older incorrectly reorder code without it
     604                asm volatile("": : :"memory");
     605                if(l != __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST)) { Pause(); continue; }
     606                return [idle, total, proc];
    803607        }
    804608}
     
    814618        // the globalAbort flag is true.
    815619        lock( kernel_abort_lock __cfaabi_dbg_ctx2 );
     620
     621        // disable interrupts, it no longer makes sense to try to interrupt this processor
     622        disable_interrupts();
    816623
    817624        // first task to abort ?
     
    831638        }
    832639
    833         return kernelTLS.this_thread;
     640        return __cfaabi_tls.this_thread;
    834641}
    835642
    836643void kernel_abort_msg( void * kernel_data, char * abort_text, int abort_text_size ) {
    837         $thread * thrd = kernel_data;
     644        $thread * thrd = ( $thread * ) kernel_data;
    838645
    839646        if(thrd) {
     
    856663
    857664int kernel_abort_lastframe( void ) __attribute__ ((__nothrow__)) {
    858         return get_coroutine(kernelTLS.this_thread) == get_coroutine(mainThread) ? 4 : 2;
     665        return get_coroutine(kernelTLS().this_thread) == get_coroutine(mainThread) ? 4 : 2;
    859666}
    860667
     
    883690void ^?{}(semaphore & this) {}
    884691
    885 void P(semaphore & this) with( this ){
     692bool P(semaphore & this) with( this ){
    886693        lock( lock __cfaabi_dbg_ctx2 );
    887694        count -= 1;
    888695        if ( count < 0 ) {
    889696                // queue current task
    890                 append( waiting, kernelTLS.this_thread );
     697                append( waiting, active_thread() );
    891698
    892699                // atomically release spin lock and block
    893700                unlock( lock );
    894701                park();
     702                return true;
    895703        }
    896704        else {
    897705            unlock( lock );
    898         }
    899 }
    900 
    901 void V(semaphore & this) with( this ) {
     706            return false;
     707        }
     708}
     709
     710bool V(semaphore & this) with( this ) {
    902711        $thread * thrd = 0p;
    903712        lock( lock __cfaabi_dbg_ctx2 );
     
    912721        // make new owner
    913722        unpark( thrd );
    914 }
    915 
    916 //-----------------------------------------------------------------------------
    917 // Global Queues
    918 void doregister( cluster     & cltr ) {
    919         lock      ( __cfa_dbg_global_clusters.lock __cfaabi_dbg_ctx2);
    920         push_front( __cfa_dbg_global_clusters.list, cltr );
    921         unlock    ( __cfa_dbg_global_clusters.lock );
    922 }
    923 
    924 void unregister( cluster     & cltr ) {
    925         lock  ( __cfa_dbg_global_clusters.lock __cfaabi_dbg_ctx2);
    926         remove( __cfa_dbg_global_clusters.list, cltr );
    927         unlock( __cfa_dbg_global_clusters.lock );
    928 }
    929 
    930 void doregister( cluster * cltr, $thread & thrd ) {
    931         lock      (cltr->thread_list_lock __cfaabi_dbg_ctx2);
    932         cltr->nthreads += 1;
    933         push_front(cltr->threads, thrd);
    934         unlock    (cltr->thread_list_lock);
    935 }
    936 
    937 void unregister( cluster * cltr, $thread & thrd ) {
    938         lock  (cltr->thread_list_lock __cfaabi_dbg_ctx2);
    939         remove(cltr->threads, thrd );
    940         cltr->nthreads -= 1;
    941         unlock(cltr->thread_list_lock);
    942 }
    943 
    944 void doregister( cluster * cltr, processor * proc ) {
    945         lock      (cltr->proc_list_lock __cfaabi_dbg_ctx2);
    946         cltr->nprocessors += 1;
    947         push_front(cltr->procs, *proc);
    948         unlock    (cltr->proc_list_lock);
    949 }
    950 
    951 void unregister( cluster * cltr, processor * proc ) {
    952         lock  (cltr->proc_list_lock __cfaabi_dbg_ctx2);
    953         remove(cltr->procs, *proc );
    954         cltr->nprocessors -= 1;
    955         unlock(cltr->proc_list_lock);
     723
     724        return thrd != 0p;
     725}
     726
     727bool V(semaphore & this, unsigned diff) with( this ) {
     728        $thread * thrd = 0p;
     729        lock( lock __cfaabi_dbg_ctx2 );
     730        int release = max(-count, (int)diff);
     731        count += diff;
     732        for(release) {
     733                unpark( pop_head( waiting ) );
     734        }
     735
     736        unlock( lock );
     737
     738        return thrd != 0p;
    956739}
    957740
     
    960743__cfaabi_dbg_debug_do(
    961744        extern "C" {
    962                 void __cfaabi_dbg_record(__spinlock_t & this, const char prev_name[]) {
     745                void __cfaabi_dbg_record_lock(__spinlock_t & this, const char prev_name[]) {
    963746                        this.prev_name = prev_name;
    964                         this.prev_thrd = kernelTLS.this_thread;
     747                        this.prev_thrd = kernelTLS().this_thread;
    965748                }
    966749        }
     
    972755        return true;
    973756}
     757
     758//-----------------------------------------------------------------------------
     759// Statistics
     760#if !defined(__CFA_NO_STATISTICS__)
     761        void print_halts( processor & this ) {
     762                this.print_halts = true;
     763        }
     764
     765        void print_stats_now( cluster & this, int flags ) {
     766                __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this );
     767        }
     768
     769        extern int __print_alarm_stats;
     770        void print_alarm_stats() {
     771                __print_alarm_stats = -1;
     772        }
     773#endif
    974774// Local Variables: //
    975775// mode: c //
  • libcfa/src/concurrency/kernel.hfa

    r3c64c668 r58fe85a  
    1616#pragma once
    1717
    18 #include <stdbool.h>
    19 
    2018#include "invoke.h"
    2119#include "time_t.hfa"
    2220#include "coroutine.hfa"
    2321
     22#include "containers/list.hfa"
     23
    2424extern "C" {
    25 #include <pthread.h>
    26 #include <semaphore.h>
     25        #include <bits/pthreadtypes.h>
     26        #include <linux/types.h>
    2727}
    2828
     
    3737void  ?{}(semaphore & this, int count = 1);
    3838void ^?{}(semaphore & this);
    39 void   P (semaphore & this);
    40 void   V (semaphore & this);
     39bool   P (semaphore & this);
     40bool   V (semaphore & this);
     41bool   V (semaphore & this, unsigned count);
    4142
    4243
     
    4546extern struct cluster * mainCluster;
    4647
    47 // Processor
     48// Processor id, required for scheduling threads
     49struct __processor_id_t {
     50        unsigned id:24;
     51        bool full_proc:1;
     52
     53        #if !defined(__CFA_NO_STATISTICS__)
     54                struct __stats_t * stats;
     55        #endif
     56};
     57
    4858coroutine processorCtx_t {
    4959        struct processor * proc;
     
    5161
    5262// Wrapper around kernel threads
    53 struct processor {
     63struct __attribute__((aligned(128))) processor {
    5464        // Main state
     65        inline __processor_id_t;
     66
     67        // Cluster from which to get threads
     68        struct cluster * cltr;
     69
     70        // Set to true to notify the processor should terminate
     71        volatile bool do_terminate;
     72
    5573        // Coroutine ctx who does keeps the state of the processor
    5674        struct processorCtx_t runner;
    5775
    58         // Cluster from which to get threads
    59         struct cluster * cltr;
    60 
    6176        // Name of the processor
    6277        const char * name;
     
    6479        // Handle to pthreads
    6580        pthread_t kernel_thread;
    66 
    67         // RunThread data
    68         // Action to do after a thread is ran
    69         $thread * destroyer;
    7081
    7182        // Preemption data
     
    7687        bool pending_preemption;
    7788
    78         // Idle lock
    79         __bin_sem_t idleLock;
    80 
    81         // Termination
    82         // Set to true to notify the processor should terminate
    83         volatile bool do_terminate;
    84 
    85         // Termination synchronisation
     89        // Idle lock (kernel semaphore)
     90        __bin_sem_t idle;
     91
     92        // Termination synchronisation (user semaphore)
    8693        semaphore terminated;
    8794
     
    9097
    9198        // Link lists fields
    92         struct __dbg_node_proc {
    93                 struct processor * next;
    94                 struct processor * prev;
    95         } node;
     99        DLISTED_MGD_IMPL_IN(processor)
     100
     101        #if !defined(__CFA_NO_STATISTICS__)
     102                int print_stats;
     103                bool print_halts;
     104        #endif
    96105
    97106#ifdef __CFA_DEBUG__
     
    108117static inline void  ?{}(processor & this, const char name[]) { this{name, *mainCluster }; }
    109118
    110 static inline [processor *&, processor *& ] __get( processor & this ) __attribute__((const)) { return this.node.[next, prev]; }
     119DLISTED_MGD_IMPL_OUT(processor)
     120
     121//-----------------------------------------------------------------------------
     122// I/O
     123struct __io_data;
     124
     125// IO poller user-thread
     126// Not using the "thread" keyword because we want to control
     127// more carefully when to start/stop it
     128struct $io_ctx_thread {
     129        struct __io_data * ring;
     130        single_sem sem;
     131        volatile bool done;
     132        $thread self;
     133};
     134
     135
     136struct io_context {
     137        $io_ctx_thread thrd;
     138};
     139
     140struct io_context_params {
     141        int num_entries;
     142        int num_ready;
     143        int submit_aff;
     144        bool eager_submits:1;
     145        bool poller_submits:1;
     146        bool poll_submit:1;
     147        bool poll_complete:1;
     148};
     149
     150void  ?{}(io_context_params & this);
     151
     152void  ?{}(io_context & this, struct cluster & cl);
     153void  ?{}(io_context & this, struct cluster & cl, const io_context_params & params);
     154void ^?{}(io_context & this);
     155
     156struct io_cancellation {
     157        __u64 target;
     158};
     159
     160static inline void  ?{}(io_cancellation & this) { this.target = -1u; }
     161static inline void ^?{}(io_cancellation &) {}
     162bool cancel(io_cancellation & this);
     163
     164//-----------------------------------------------------------------------------
     165// Cluster Tools
     166
     167// Intrusives lanes which are used by the relaxed ready queue
     168struct __attribute__((aligned(128))) __intrusive_lane_t;
     169void  ?{}(__intrusive_lane_t & this);
     170void ^?{}(__intrusive_lane_t & this);
     171
     172// Counter used for wether or not the lanes are all empty
     173struct __attribute__((aligned(128))) __snzi_node_t;
     174struct __snzi_t {
     175        unsigned mask;
     176        int root;
     177        __snzi_node_t * nodes;
     178};
     179
     180void  ?{}( __snzi_t & this, unsigned depth );
     181void ^?{}( __snzi_t & this );
     182
     183//TODO adjust cache size to ARCHITECTURE
     184// Structure holding the relaxed ready queue
     185struct __ready_queue_t {
     186        // Data tracking how many/which lanes are used
     187        // Aligned to 128 for cache locality
     188        __snzi_t snzi;
     189
     190        // Data tracking the actual lanes
     191        // On a seperate cacheline from the used struct since
     192        // used can change on each push/pop but this data
     193        // only changes on shrink/grow
     194        struct {
     195                // Arary of lanes
     196                __intrusive_lane_t * volatile data;
     197
     198                // Number of lanes (empty or not)
     199                volatile size_t count;
     200        } lanes;
     201};
     202
     203void  ?{}(__ready_queue_t & this);
     204void ^?{}(__ready_queue_t & this);
     205
     206// Idle Sleep
     207struct __cluster_idles {
     208        // Spin lock protecting the queue
     209        volatile uint64_t lock;
     210
     211        // Total number of processors
     212        unsigned total;
     213
     214        // Total number of idle processors
     215        unsigned idle;
     216
     217        // List of idle processors
     218        dlist(processor, processor) list;
     219};
    111220
    112221//-----------------------------------------------------------------------------
    113222// Cluster
    114 struct cluster {
    115         // Ready queue locks
    116         __spinlock_t ready_queue_lock;
    117 
     223struct __attribute__((aligned(128))) cluster {
    118224        // Ready queue for threads
    119         __queue_t($thread) ready_queue;
     225        __ready_queue_t ready_queue;
    120226
    121227        // Name of the cluster
     
    125231        Duration preemption_rate;
    126232
    127         // List of processors
    128         __spinlock_t proc_list_lock;
    129         __dllist_t(struct processor) procs;
    130         __dllist_t(struct processor) idles;
    131         unsigned int nprocessors;
     233        // List of idle processors
     234        __cluster_idles idles;
    132235
    133236        // List of threads
     
    141244                cluster * prev;
    142245        } node;
     246
     247        struct {
     248                io_context * ctxs;
     249                unsigned cnt;
     250        } io;
     251
     252        #if !defined(__CFA_NO_STATISTICS__)
     253                struct __stats_t * stats;
     254                int print_stats;
     255        #endif
    143256};
    144257extern Duration default_preemption();
    145258
    146 void ?{} (cluster & this, const char name[], Duration preemption_rate);
     259void ?{} (cluster & this, const char name[], Duration preemption_rate, unsigned num_io, const io_context_params & io_params);
    147260void ^?{}(cluster & this);
    148261
    149 static inline void ?{} (cluster & this)                           { this{"Anonymous Cluster", default_preemption()}; }
    150 static inline void ?{} (cluster & this, Duration preemption_rate) { this{"Anonymous Cluster", preemption_rate}; }
    151 static inline void ?{} (cluster & this, const char name[])        { this{name, default_preemption()}; }
     262static inline void ?{} (cluster & this)                                            { io_context_params default_params;    this{"Anonymous Cluster", default_preemption(), 1, default_params}; }
     263static inline void ?{} (cluster & this, Duration preemption_rate)                  { io_context_params default_params;    this{"Anonymous Cluster", preemption_rate, 1, default_params}; }
     264static inline void ?{} (cluster & this, const char name[])                         { io_context_params default_params;    this{name, default_preemption(), 1, default_params}; }
     265static inline void ?{} (cluster & this, unsigned num_io)                           { io_context_params default_params;    this{"Anonymous Cluster", default_preemption(), num_io, default_params}; }
     266static inline void ?{} (cluster & this, Duration preemption_rate, unsigned num_io) { io_context_params default_params;    this{"Anonymous Cluster", preemption_rate, num_io, default_params}; }
     267static inline void ?{} (cluster & this, const char name[], unsigned num_io)        { io_context_params default_params;    this{name, default_preemption(), num_io, default_params}; }
     268static inline void ?{} (cluster & this, const io_context_params & io_params)                                            { this{"Anonymous Cluster", default_preemption(), 1, io_params}; }
     269static inline void ?{} (cluster & this, Duration preemption_rate, const io_context_params & io_params)                  { this{"Anonymous Cluster", preemption_rate, 1, io_params}; }
     270static inline void ?{} (cluster & this, const char name[], const io_context_params & io_params)                         { this{name, default_preemption(), 1, io_params}; }
     271static inline void ?{} (cluster & this, unsigned num_io, const io_context_params & io_params)                           { this{"Anonymous Cluster", default_preemption(), num_io, io_params}; }
     272static inline void ?{} (cluster & this, Duration preemption_rate, unsigned num_io, const io_context_params & io_params) { this{"Anonymous Cluster", preemption_rate, num_io, io_params}; }
     273static inline void ?{} (cluster & this, const char name[], unsigned num_io, const io_context_params & io_params)        { this{name, default_preemption(), num_io, io_params}; }
    152274
    153275static inline [cluster *&, cluster *& ] __get( cluster & this ) __attribute__((const)) { return this.node.[next, prev]; }
    154276
    155 static inline struct processor * active_processor() { return TL_GET( this_processor ); } // UNSAFE
    156 static inline struct cluster   * active_cluster  () { return TL_GET( this_processor )->cltr; }
     277static inline struct processor * active_processor() { return publicTLS_get( this_processor ); } // UNSAFE
     278static inline struct cluster   * active_cluster  () { return publicTLS_get( this_processor )->cltr; }
     279
     280#if !defined(__CFA_NO_STATISTICS__)
     281        void print_stats_now( cluster & this, int flags );
     282
     283        static inline void print_stats_at_exit( cluster & this, int flags ) {
     284                this.print_stats |= flags;
     285        }
     286
     287        static inline void print_stats_at_exit( processor & this, int flags ) {
     288                this.print_stats |= flags;
     289        }
     290
     291        void print_halts( processor & this );
     292#endif
    157293
    158294// Local Variables: //
  • libcfa/src/concurrency/kernel_private.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Mon Feb 13 12:27:26 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Nov 30 19:25:02 2019
    13 // Update Count     : 8
     12// Last Modified On : Wed Aug 12 08:21:33 2020
     13// Update Count     : 9
    1414//
    1515
     
    2020
    2121#include "alarm.hfa"
    22 
     22#include "stats.hfa"
    2323
    2424//-----------------------------------------------------------------------------
    2525// Scheduler
     26
     27struct __attribute__((aligned(128))) __scheduler_lock_id_t;
    2628
    2729extern "C" {
     
    3133}
    3234
    33 void __schedule_thread( $thread * ) __attribute__((nonnull (1)));
    34 
    35 //Block current thread and release/wake-up the following resources
    36 void __leave_thread() __attribute__((noreturn));
     35void __schedule_thread( $thread * )
     36#if defined(NDEBUG) || (!defined(__CFA_DEBUG__) && !defined(__CFA_VERIFY__))
     37        __attribute__((nonnull (1)))
     38#endif
     39;
     40
     41extern bool __preemption_enabled();
     42
     43//release/wake-up the following resources
     44void __thread_finish( $thread * thrd );
    3745
    3846//-----------------------------------------------------------------------------
     
    4149
    4250void * __create_pthread( pthread_t *, void * (*)(void *), void * );
    43 
    44 static inline void wake_fast(processor * this) {
    45         __cfaabi_dbg_print_safe("Kernel : Waking up processor %p\n", this);
    46         post( this->idleLock );
    47 }
    48 
    49 static inline void wake(processor * this) {
    50         disable_interrupts();
    51         wake_fast(this);
    52         enable_interrupts( __cfaabi_dbg_ctx );
    53 }
    54 
    55 struct event_kernel_t {
    56         alarm_list_t alarms;
    57         __spinlock_t lock;
    58 };
    59 
    60 extern event_kernel_t * event_kernel;
    61 
    62 struct __cfa_kernel_preemption_state_t {
    63         bool enabled;
    64         bool in_progress;
    65         unsigned short disable_count;
    66 };
    67 
    68 extern volatile thread_local __cfa_kernel_preemption_state_t preemption_state __attribute__ ((tls_model ( "initial-exec" )));
     51void __destroy_pthread( pthread_t pthread, void * stack, void ** retval );
     52
     53
     54
     55extern cluster * mainCluster;
    6956
    7057//-----------------------------------------------------------------------------
     
    7966)
    8067
     68#define TICKET_BLOCKED (-1) // thread is blocked
     69#define TICKET_RUNNING ( 0) // thread is running
     70#define TICKET_UNBLOCK ( 1) // thread should ignore next block
     71
    8172//-----------------------------------------------------------------------------
    8273// Utils
    83 #define KERNEL_STORAGE(T,X) static char storage_##X[sizeof(T)]
    84 
    85 static inline uint32_t __tls_rand() {
    86         kernelTLS.rand_seed ^= kernelTLS.rand_seed << 6;
    87         kernelTLS.rand_seed ^= kernelTLS.rand_seed >> 21;
    88         kernelTLS.rand_seed ^= kernelTLS.rand_seed << 7;
    89         return kernelTLS.rand_seed;
    90 }
    91 
    92 
    93 void doregister( struct cluster & cltr );
    94 void unregister( struct cluster & cltr );
    95 
    9674void doregister( struct cluster * cltr, struct $thread & thrd );
    9775void unregister( struct cluster * cltr, struct $thread & thrd );
    9876
    99 void doregister( struct cluster * cltr, struct processor * proc );
    100 void unregister( struct cluster * cltr, struct processor * proc );
     77//-----------------------------------------------------------------------------
     78// I/O
     79void ^?{}(io_context & this, bool );
     80
     81//=======================================================================
     82// Cluster lock API
     83//=======================================================================
     84// Cells use by the reader writer lock
     85// while not generic it only relies on a opaque pointer
     86struct __attribute__((aligned(128))) __scheduler_lock_id_t {
     87        // Spin lock used as the underlying lock
     88        volatile bool lock;
     89
     90        // Handle pointing to the proc owning this cell
     91        // Used for allocating cells and debugging
     92        __processor_id_t * volatile handle;
     93
     94        #ifdef __CFA_WITH_VERIFY__
     95                // Debug, check if this is owned for reading
     96                bool owned;
     97        #endif
     98};
     99
     100static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t));
     101
     102// Lock-Free registering/unregistering of threads
     103// Register a processor to a given cluster and get its unique id in return
     104unsigned doregister( struct __processor_id_t * proc );
     105
     106// Unregister a processor from a given cluster using its id, getting back the original pointer
     107void     unregister( struct __processor_id_t * proc );
     108
     109//-----------------------------------------------------------------------
     110// Cluster idle lock/unlock
     111static inline void lock(__cluster_idles & this) {
     112        for() {
     113                uint64_t l = this.lock;
     114                if(
     115                        (0 == (l % 2))
     116                        && __atomic_compare_exchange_n(&this.lock, &l, l + 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
     117                ) return;
     118                Pause();
     119        }
     120}
     121
     122static inline void unlock(__cluster_idles & this) {
     123        /* paranoid */ verify( 1 == (this.lock % 2) );
     124        __atomic_fetch_add( &this.lock, 1, __ATOMIC_SEQ_CST );
     125}
     126
     127//=======================================================================
     128// Reader-writer lock implementation
     129// Concurrent with doregister/unregister,
     130//    i.e., threads can be added at any point during or between the entry/exit
     131
     132//-----------------------------------------------------------------------
     133// simple spinlock underlying the RWLock
     134// Blocking acquire
     135static inline void __atomic_acquire(volatile bool * ll) {
     136        while( __builtin_expect(__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST), false) ) {
     137                while(__atomic_load_n(ll, (int)__ATOMIC_RELAXED))
     138                        Pause();
     139        }
     140        /* paranoid */ verify(*ll);
     141}
     142
     143// Non-Blocking acquire
     144static inline bool __atomic_try_acquire(volatile bool * ll) {
     145        return !__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST);
     146}
     147
     148// Release
     149static inline void __atomic_unlock(volatile bool * ll) {
     150        /* paranoid */ verify(*ll);
     151        __atomic_store_n(ll, (bool)false, __ATOMIC_RELEASE);
     152}
     153
     154//-----------------------------------------------------------------------
     155// Reader-Writer lock protecting the ready-queues
     156// while this lock is mostly generic some aspects
     157// have been hard-coded to for the ready-queue for
     158// simplicity and performance
     159struct __scheduler_RWLock_t {
     160        // total cachelines allocated
     161        unsigned int max;
     162
     163        // cachelines currently in use
     164        volatile unsigned int alloc;
     165
     166        // cachelines ready to itereate over
     167        // (!= to alloc when thread is in second half of doregister)
     168        volatile unsigned int ready;
     169
     170        // writer lock
     171        volatile bool lock;
     172
     173        // data pointer
     174        __scheduler_lock_id_t * data;
     175};
     176
     177void  ?{}(__scheduler_RWLock_t & this);
     178void ^?{}(__scheduler_RWLock_t & this);
     179
     180extern __scheduler_RWLock_t * __scheduler_lock;
     181
     182//-----------------------------------------------------------------------
     183// Reader side : acquire when using the ready queue to schedule but not
     184//  creating/destroying queues
     185static inline void ready_schedule_lock(void) with(*__scheduler_lock) {
     186        /* paranoid */ verify( ! __preemption_enabled() );
     187        /* paranoid */ verify( kernelTLS().this_proc_id );
     188
     189        unsigned iproc = kernelTLS().this_proc_id->id;
     190        /*paranoid*/ verify(data[iproc].handle == kernelTLS().this_proc_id);
     191        /*paranoid*/ verify(iproc < ready);
     192
     193        // Step 1 : make sure no writer are in the middle of the critical section
     194        while(__atomic_load_n(&lock, (int)__ATOMIC_RELAXED))
     195                Pause();
     196
     197        // Fence needed because we don't want to start trying to acquire the lock
     198        // before we read a false.
     199        // Not needed on x86
     200        // std::atomic_thread_fence(std::memory_order_seq_cst);
     201
     202        // Step 2 : acquire our local lock
     203        __atomic_acquire( &data[iproc].lock );
     204        /*paranoid*/ verify(data[iproc].lock);
     205
     206        #ifdef __CFA_WITH_VERIFY__
     207                // Debug, check if this is owned for reading
     208                data[iproc].owned = true;
     209        #endif
     210}
     211
     212static inline void ready_schedule_unlock(void) with(*__scheduler_lock) {
     213        /* paranoid */ verify( ! __preemption_enabled() );
     214        /* paranoid */ verify( kernelTLS().this_proc_id );
     215
     216        unsigned iproc = kernelTLS().this_proc_id->id;
     217        /*paranoid*/ verify(data[iproc].handle == kernelTLS().this_proc_id);
     218        /*paranoid*/ verify(iproc < ready);
     219        /*paranoid*/ verify(data[iproc].lock);
     220        /*paranoid*/ verify(data[iproc].owned);
     221        #ifdef __CFA_WITH_VERIFY__
     222                // Debug, check if this is owned for reading
     223                data[iproc].owned = false;
     224        #endif
     225        __atomic_unlock(&data[iproc].lock);
     226}
     227
     228#ifdef __CFA_WITH_VERIFY__
     229        static inline bool ready_schedule_islocked(void) {
     230                /* paranoid */ verify( ! __preemption_enabled() );
     231                /*paranoid*/ verify( kernelTLS().this_proc_id );
     232                __processor_id_t * proc = kernelTLS().this_proc_id;
     233                return __scheduler_lock->data[proc->id].owned;
     234        }
     235
     236        static inline bool ready_mutate_islocked() {
     237                return __scheduler_lock->lock;
     238        }
     239#endif
     240
     241//-----------------------------------------------------------------------
     242// Writer side : acquire when changing the ready queue, e.g. adding more
     243//  queues or removing them.
     244uint_fast32_t ready_mutate_lock( void );
     245
     246void ready_mutate_unlock( uint_fast32_t /* value returned by lock */ );
     247
     248//=======================================================================
     249// Ready-Queue API
     250//-----------------------------------------------------------------------
     251// pop thread from the ready queue of a cluster
     252// returns 0p if empty
     253__attribute__((hot)) bool query(struct cluster * cltr);
     254
     255//-----------------------------------------------------------------------
     256// push thread onto a ready queue for a cluster
     257// returns true if the list was previously empty, false otherwise
     258__attribute__((hot)) bool push(struct cluster * cltr, struct $thread * thrd);
     259
     260//-----------------------------------------------------------------------
     261// pop thread from the ready queue of a cluster
     262// returns 0p if empty
     263// May return 0p spuriously
     264__attribute__((hot)) struct $thread * pop(struct cluster * cltr);
     265
     266//-----------------------------------------------------------------------
     267// pop thread from the ready queue of a cluster
     268// returns 0p if empty
     269// guaranteed to find any threads added before this call
     270__attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr);
     271
     272//-----------------------------------------------------------------------
     273// remove thread from the ready queue of a cluster
     274// returns bool if it wasn't found
     275bool remove_head(struct cluster * cltr, struct $thread * thrd);
     276
     277//-----------------------------------------------------------------------
     278// Increase the width of the ready queue (number of lanes) by 4
     279void ready_queue_grow  (struct cluster * cltr, int target);
     280
     281//-----------------------------------------------------------------------
     282// Decrease the width of the ready queue (number of lanes) by 4
     283void ready_queue_shrink(struct cluster * cltr, int target);
     284
    101285
    102286// Local Variables: //
  • libcfa/src/concurrency/monitor.cfa

    r3c64c668 r58fe85a  
    8282// Enter single monitor
    8383static void __enter( $monitor * this, const __monitor_group_t & group ) {
     84        $thread * thrd = active_thread();
     85
    8486        // Lock the monitor spinlock
    8587        lock( this->lock __cfaabi_dbg_ctx2 );
    86         // Interrupts disable inside critical section
    87         $thread * thrd = kernelTLS.this_thread;
    8888
    8989        __cfaabi_dbg_print_safe( "Kernel : %10p Entering mon %p (%p)\n", thrd, this, this->owner);
    9090
    91         if( !this->owner ) {
     91        if( unlikely(0 != (0x1 & (uintptr_t)this->owner)) ) {
     92                abort( "Attempt by thread \"%.256s\" (%p) to access joined monitor %p.", thrd->self_cor.name, thrd, this );
     93        }
     94        else if( !this->owner ) {
    9295                // No one has the monitor, just take it
    9396                __set_owner( this, thrd );
     
    114117
    115118                // Some one else has the monitor, wait in line for it
    116                 /* paranoid */ verify( thrd->next == 0p );
     119                /* paranoid */ verify( thrd->link.next == 0p );
    117120                append( this->entry_queue, thrd );
    118                 /* paranoid */ verify( thrd->next == 1p );
     121                /* paranoid */ verify( thrd->link.next == 1p );
    119122
    120123                unlock( this->lock );
     
    123126                __cfaabi_dbg_print_safe( "Kernel : %10p Entered  mon %p\n", thrd, this);
    124127
    125                 /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
     128                /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
    126129                return;
    127130        }
     
    129132        __cfaabi_dbg_print_safe( "Kernel : %10p Entered  mon %p\n", thrd, this);
    130133
    131         /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
     134        /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
    132135        /* paranoid */ verify( this->lock.lock );
    133136
     
    137140}
    138141
    139 static void __dtor_enter( $monitor * this, fptr_t func ) {
     142static void __dtor_enter( $monitor * this, fptr_t func, bool join ) {
     143        $thread * thrd = active_thread();
     144        #if defined( __CFA_WITH_VERIFY__ )
     145                bool is_thrd = this == &thrd->self_mon;
     146        #endif
     147
    140148        // Lock the monitor spinlock
    141149        lock( this->lock __cfaabi_dbg_ctx2 );
    142         // Interrupts disable inside critical section
    143         $thread * thrd = kernelTLS.this_thread;
    144150
    145151        __cfaabi_dbg_print_safe( "Kernel : %10p Entering dtor for mon %p (%p)\n", thrd, this, this->owner);
     
    152158                __set_owner( this, thrd );
    153159
    154                 verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
     160                /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
     161                /* paranoid */ verify( !is_thrd || thrd->state == Halted || thrd->state == Cancelled );
    155162
    156163                unlock( this->lock );
    157164                return;
    158165        }
    159         else if( this->owner == thrd) {
     166        else if( this->owner == thrd && !join) {
    160167                // We already have the monitor... but where about to destroy it so the nesting will fail
    161168                // Abort!
    162169                abort( "Attempt to destroy monitor %p by thread \"%.256s\" (%p) in nested mutex.", this, thrd->self_cor.name, thrd );
    163170        }
     171        // SKULLDUGGERY: join will act as a dtor so it would normally trigger to above check
     172        // because join will not release the monitor after it executed.
     173        // to avoid that it sets the owner to the special value thrd | 1p before exiting
     174        else if( this->owner == ($thread*)(1 | (uintptr_t)thrd) ) {
     175                // restore the owner and just return
     176                __cfaabi_dbg_print_safe( "Kernel : Destroying free mon %p\n", this);
     177
     178                // No one has the monitor, just take it
     179                __set_owner( this, thrd );
     180
     181                /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
     182                /* paranoid */ verify( !is_thrd || thrd->state == Halted || thrd->state == Cancelled );
     183
     184                unlock( this->lock );
     185                return;
     186        }
     187
     188        // The monitor is busy, if this is a thread and the thread owns itself, it better be active
     189        /* paranoid */ verify( !is_thrd || this->owner != thrd || (thrd->state != Halted && thrd->state != Cancelled) );
    164190
    165191        __lock_size_t count = 1;
     
    183209
    184210                // Release the next thread
    185                 /* paranoid */ verifyf( urgent->owner->waiting_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
     211                /* paranoid */ verifyf( urgent->owner->waiting_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
    186212                unpark( urgent->owner->waiting_thread );
    187213
     
    190216
    191217                // Some one was waiting for us, enter
    192                 /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
     218                /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
     219
     220                __cfaabi_dbg_print_safe( "Kernel : Destroying %p\n", this);
     221                return;
    193222        }
    194223        else {
     
    199228
    200229                // Some one else has the monitor, wait in line for it
    201                 /* paranoid */ verify( thrd->next == 0p );
     230                /* paranoid */ verify( thrd->link.next == 0p );
    202231                append( this->entry_queue, thrd );
    203                 /* paranoid */ verify( thrd->next == 1p );
     232                /* paranoid */ verify( thrd->link.next == 1p );
    204233                unlock( this->lock );
    205234
     
    207236                park();
    208237
    209                 /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
     238                /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
    210239                return;
    211240        }
    212 
    213         __cfaabi_dbg_print_safe( "Kernel : Destroying %p\n", this);
    214 
    215241}
    216242
     
    220246        lock( this->lock __cfaabi_dbg_ctx2 );
    221247
    222         __cfaabi_dbg_print_safe( "Kernel : %10p Leaving mon %p (%p)\n", kernelTLS.this_thread, this, this->owner);
    223 
    224         /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
     248        __cfaabi_dbg_print_safe( "Kernel : %10p Leaving mon %p (%p)\n", active_thread(), this, this->owner);
     249
     250        /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
    225251
    226252        // Leaving a recursion level, decrement the counter
     
    251277
    252278// Leave single monitor for the last time
    253 void __dtor_leave( $monitor * this ) {
     279void __dtor_leave( $monitor * this, bool join ) {
    254280        __cfaabi_dbg_debug_do(
    255                 if( TL_GET( this_thread ) != this->owner ) {
    256                         abort( "Destroyed monitor %p has inconsistent owner, expected %p got %p.\n", this, TL_GET( this_thread ), this->owner);
     281                if( active_thread() != this->owner ) {
     282                        abort( "Destroyed monitor %p has inconsistent owner, expected %p got %p.\n", this, active_thread(), this->owner);
    257283                }
    258                 if( this->recursion != 1 ) {
     284                if( this->recursion != 1  && !join ) {
    259285                        abort( "Destroyed monitor %p has %d outstanding nested calls.\n", this, this->recursion - 1);
    260286                }
    261287        )
    262 }
    263 
    264 extern "C" {
    265         // Leave the thread monitor
    266         // last routine called by a thread.
    267         // Should never return
    268         void __cfactx_thrd_leave() {
    269                 $thread * thrd = TL_GET( this_thread );
    270                 $monitor * this = &thrd->self_mon;
    271 
    272                 // Lock the monitor now
    273                 lock( this->lock __cfaabi_dbg_ctx2 );
    274 
    275                 disable_interrupts();
    276 
    277                 thrd->state = Halted;
    278 
    279                 /* paranoid */ verifyf( thrd == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", thrd, this->owner, this->recursion, this );
    280 
    281                 // Leaving a recursion level, decrement the counter
    282                 this->recursion -= 1;
    283 
    284                 // If we haven't left the last level of recursion
    285                 // it must mean there is an error
    286                 if( this->recursion != 0) { abort( "Thread internal monitor has unbalanced recursion" ); }
    287 
    288                 // Fetch the next thread, can be null
    289                 $thread * new_owner = next_thread( this );
    290 
    291                 // Release the monitor lock
    292                 unlock( this->lock );
    293 
    294                 // Unpark the next owner if needed
    295                 /* paranoid */ verifyf( !new_owner || new_owner == this->owner, "Expected owner to be %p, got %p (m: %p)", new_owner, this->owner, this );
    296                 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
    297                 /* paranoid */ verify( ! kernelTLS.this_processor->destroyer );
    298                 /* paranoid */ verify( thrd->state == Halted );
    299 
    300                 kernelTLS.this_processor->destroyer = new_owner;
    301 
    302                 // Leave the thread
    303                 __leave_thread();
    304 
    305                 // Control flow should never reach here!
    306         }
     288
     289        this->owner = ($thread*)(1 | (uintptr_t)this->owner);
     290}
     291
     292void __thread_finish( $thread * thrd ) {
     293        $monitor * this = &thrd->self_mon;
     294
     295        // Lock the monitor now
     296        /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd->canary );
     297        /* paranoid */ verify( this->lock.lock );
     298        /* paranoid */ verify( thrd->context.SP );
     299        /* paranoid */ verifyf( ((uintptr_t)thrd->context.SP) > ((uintptr_t)__get_stack(thrd->curr_cor)->limit), "ERROR : $thread %p has been corrupted.\n StackPointer too large.\n", thrd );
     300        /* paranoid */ verifyf( ((uintptr_t)thrd->context.SP) < ((uintptr_t)__get_stack(thrd->curr_cor)->base ), "ERROR : $thread %p has been corrupted.\n StackPointer too small.\n", thrd );
     301        /* paranoid */ verify( ! __preemption_enabled() );
     302
     303        /* paranoid */ verifyf( thrd == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", thrd, this->owner, this->recursion, this );
     304        /* paranoid */ verify( thrd->state == Halting );
     305        /* paranoid */ verify( this->recursion == 1 );
     306
     307        // Leaving a recursion level, decrement the counter
     308        this->recursion -= 1;
     309        this->owner = 0p;
     310
     311        // Fetch the next thread, can be null
     312        $thread * new_owner = next_thread( this );
     313
     314        // Mark the state as fully halted
     315        thrd->state = Halted;
     316
     317        // Release the monitor lock
     318        unlock( this->lock );
     319
     320        // Unpark the next owner if needed
     321        /* paranoid */ verifyf( !new_owner || new_owner == this->owner, "Expected owner to be %p, got %p (m: %p)", new_owner, this->owner, this );
     322        /* paranoid */ verify( ! __preemption_enabled() );
     323        /* paranoid */ verify( thrd->state == Halted );
     324        unpark( new_owner );
    307325}
    308326
     
    326344// Sorts monitors before entering
    327345void ?{}( monitor_guard_t & this, $monitor * m [], __lock_size_t count, fptr_t func ) {
    328         $thread * thrd = TL_GET( this_thread );
     346        $thread * thrd = active_thread();
    329347
    330348        // Store current array
     
    361379
    362380        // Restore thread context
    363         TL_GET( this_thread )->monitors = this.prev;
     381        active_thread()->monitors = this.prev;
    364382}
    365383
    366384// Ctor for monitor guard
    367385// Sorts monitors before entering
    368 void ?{}( monitor_dtor_guard_t & this, $monitor * m [], fptr_t func ) {
     386void ?{}( monitor_dtor_guard_t & this, $monitor * m [], fptr_t func, bool join ) {
    369387        // optimization
    370         $thread * thrd = TL_GET( this_thread );
     388        $thread * thrd = active_thread();
    371389
    372390        // Store current array
     
    376394        this.prev = thrd->monitors;
    377395
     396        // Save whether we are in a join or not
     397        this.join = join;
     398
    378399        // Update thread context (needed for conditions)
    379400        (thrd->monitors){m, 1, func};
    380401
    381         __dtor_enter( this.m, func );
     402        __dtor_enter( this.m, func, join );
    382403}
    383404
     
    385406void ^?{}( monitor_dtor_guard_t & this ) {
    386407        // Leave the monitors in order
    387         __dtor_leave( this.m );
     408        __dtor_leave( this.m, this.join );
    388409
    389410        // Restore thread context
    390         TL_GET( this_thread )->monitors = this.prev;
     411        active_thread()->monitors = this.prev;
    391412}
    392413
     
    428449
    429450        // Create the node specific to this wait operation
    430         wait_ctx( TL_GET( this_thread ), user_info );
     451        wait_ctx( active_thread(), user_info );
    431452
    432453        // Append the current wait operation to the ones already queued on the condition
     
    479500        //Some more checking in debug
    480501        __cfaabi_dbg_debug_do(
    481                 $thread * this_thrd = TL_GET( this_thread );
     502                $thread * this_thrd = active_thread();
    482503                if ( this.monitor_count != this_thrd->monitors.size ) {
    483504                        abort( "Signal on condition %p made with different number of monitor(s), expected %zi got %zi", &this, this.monitor_count, this_thrd->monitors.size );
     
    527548
    528549        // Create the node specific to this wait operation
    529         wait_ctx_primed( kernelTLS.this_thread, 0 )
     550        wait_ctx_primed( active_thread(), 0 )
    530551
    531552        //save contexts
     
    534555        //Find the thread to run
    535556        $thread * signallee = pop_head( this.blocked )->waiting_thread;
    536         /* paranoid */ verify( signallee->next == 0p );
    537557        __set_owner( monitors, count, signallee );
    538558
     
    627647
    628648                                // Create the node specific to this wait operation
    629                                 wait_ctx_primed( kernelTLS.this_thread, 0 );
     649                                wait_ctx_primed( active_thread(), 0 );
    630650
    631651                                // Save monitor states
     
    679699
    680700        // Create the node specific to this wait operation
    681         wait_ctx_primed( kernelTLS.this_thread, 0 );
     701        wait_ctx_primed( active_thread(), 0 );
    682702
    683703        monitor_save;
     
    685705
    686706        for( __lock_size_t i = 0; i < count; i++) {
    687                 verify( monitors[i]->owner == kernelTLS.this_thread );
     707                verify( monitors[i]->owner == active_thread() );
    688708        }
    689709
     
    721741static inline void __set_owner( $monitor * monitors [], __lock_size_t count, $thread * owner ) {
    722742        /* paranoid */ verify ( monitors[0]->lock.lock );
    723         /* paranoid */ verifyf( monitors[0]->owner == kernelTLS.this_thread, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, monitors[0]->owner, monitors[0]->recursion, monitors[0] );
     743        /* paranoid */ verifyf( monitors[0]->owner == active_thread(), "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), monitors[0]->owner, monitors[0]->recursion, monitors[0] );
    724744        monitors[0]->owner        = owner;
    725745        monitors[0]->recursion    = 1;
    726746        for( __lock_size_t i = 1; i < count; i++ ) {
    727747                /* paranoid */ verify ( monitors[i]->lock.lock );
    728                 /* paranoid */ verifyf( monitors[i]->owner == kernelTLS.this_thread, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, monitors[i]->owner, monitors[i]->recursion, monitors[i] );
     748                /* paranoid */ verifyf( monitors[i]->owner == active_thread(), "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), monitors[i]->owner, monitors[i]->recursion, monitors[i] );
    729749                monitors[i]->owner        = owner;
    730750                monitors[i]->recursion    = 0;
     
    752772                //regardless of if we are ready to baton pass,
    753773                //we need to set the monitor as in use
    754                 /* paranoid */ verifyf( !this->owner || kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
     774                /* paranoid */ verifyf( !this->owner || active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
    755775                __set_owner( this,  urgent->owner->waiting_thread );
    756776
     
    761781        // Get the next thread in the entry_queue
    762782        $thread * new_owner = pop_head( this->entry_queue );
    763         /* paranoid */ verifyf( !this->owner || kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );
    764         /* paranoid */ verify( !new_owner || new_owner->next == 0p );
     783        /* paranoid */ verifyf( !this->owner || active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
     784        /* paranoid */ verify( !new_owner || new_owner->link.next == 0p );
    765785        __set_owner( this, new_owner );
    766786
     
    884904        }
    885905
    886         __cfaabi_dbg_print_safe( "Kernel :  Runing %i (%p)\n", ready2run, ready2run ? node->waiting_thread : 0p );
     906        __cfaabi_dbg_print_safe( "Kernel :  Runing %i (%p)\n", ready2run, ready2run ? (thread*)node->waiting_thread : (thread*)0p );
    887907        return ready2run ? node->waiting_thread : 0p;
    888908}
    889909
    890910static inline void brand_condition( condition & this ) {
    891         $thread * thrd = TL_GET( this_thread );
     911        $thread * thrd = active_thread();
    892912        if( !this.monitors ) {
    893913                // __cfaabi_dbg_print_safe( "Branding\n" );
     
    908928        // For each thread in the entry-queue
    909929        for(    $thread ** thrd_it = &entry_queue.head;
    910                 *thrd_it != 1p;
    911                 thrd_it = &(*thrd_it)->next
     930                (*thrd_it) != 1p;
     931                thrd_it = &(*thrd_it)->link.next
    912932        ) {
    913933                // For each acceptable check if it matches
  • libcfa/src/concurrency/monitor.hfa

    r3c64c668 r58fe85a  
    5353        $monitor *    m;
    5454        __monitor_group_t prev;
     55        bool join;
    5556};
    5657
    57 void ?{}( monitor_dtor_guard_t & this, $monitor ** m, void (*func)() );
     58void ?{}( monitor_dtor_guard_t & this, $monitor ** m, void (*func)(), bool join );
    5859void ^?{}( monitor_dtor_guard_t & this );
    5960
     
    131132
    132133              void wait        ( condition & this, uintptr_t user_info = 0 );
     134static inline bool is_empty    ( condition & this ) { return this.blocked.head == 1p; }
    133135              bool signal      ( condition & this );
    134136              bool signal_block( condition & this );
    135 static inline bool is_empty    ( condition & this ) { return this.blocked.head == 1p; }
     137static inline bool signal_all  ( condition & this ) { bool ret = false; while(!is_empty(this)) { ret = signal(this) || ret; } return ret; }
    136138         uintptr_t front       ( condition & this );
    137139
  • libcfa/src/concurrency/mutex.cfa

    r3c64c668 r58fe85a  
    3030        this.lock{};
    3131        this.blocked_threads{};
     32        this.is_locked = false;
    3233}
    3334
     
    3940        lock( lock __cfaabi_dbg_ctx2 );
    4041        if( is_locked ) {
    41                 append( blocked_threads, kernelTLS.this_thread );
     42                append( blocked_threads, active_thread() );
    4243                unlock( lock );
    4344                park();
     
    8586        lock( lock __cfaabi_dbg_ctx2 );
    8687        if( owner == 0p ) {
    87                 owner = kernelTLS.this_thread;
     88                owner = active_thread();
    8889                recursion_count = 1;
    8990                unlock( lock );
    9091        }
    91         else if( owner == kernelTLS.this_thread ) {
     92        else if( owner == active_thread() ) {
    9293                recursion_count++;
    9394                unlock( lock );
    9495        }
    9596        else {
    96                 append( blocked_threads, kernelTLS.this_thread );
     97                append( blocked_threads, active_thread() );
    9798                unlock( lock );
    9899                park();
     
    104105        lock( lock __cfaabi_dbg_ctx2 );
    105106        if( owner == 0p ) {
    106                 owner = kernelTLS.this_thread;
     107                owner = active_thread();
    107108                recursion_count = 1;
    108109                ret = true;
    109110        }
    110         else if( owner == kernelTLS.this_thread ) {
     111        else if( owner == active_thread() ) {
    111112                recursion_count++;
    112113                ret = true;
     
    158159void wait(condition_variable & this) {
    159160        lock( this.lock __cfaabi_dbg_ctx2 );
    160         append( this.blocked_threads, kernelTLS.this_thread );
     161        append( this.blocked_threads, active_thread() );
    161162        unlock( this.lock );
    162163        park();
     
    166167void wait(condition_variable & this, L & l) {
    167168        lock( this.lock __cfaabi_dbg_ctx2 );
    168         append( this.blocked_threads, kernelTLS.this_thread );
     169        append( this.blocked_threads, active_thread() );
    169170        unlock(l);
    170171        unlock(this.lock);
  • libcfa/src/concurrency/preemption.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Mon Jun 5 14:20:42 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Dec  5 16:34:05 2019
    13 // Update Count     : 43
     12// Last Modified On : Fri Nov  6 07:42:13 2020
     13// Update Count     : 54
    1414//
    1515
     
    1919#include <assert.h>
    2020
    21 extern "C" {
    2221#include <errno.h>
    2322#include <stdio.h>
     
    2524#include <unistd.h>
    2625#include <limits.h>                                                                             // PTHREAD_STACK_MIN
    27 }
    2826
    2927#include "bits/signal.hfa"
     28#include "kernel_private.hfa"
    3029
    3130#if !defined(__CFA_DEFAULT_PREEMPTION__)
     
    4342// FwdDeclarations : Signal handlers
    4443static void sigHandler_ctxSwitch( __CFA_SIGPARMS__ );
     44static void sigHandler_alarm    ( __CFA_SIGPARMS__ );
    4545static void sigHandler_segv     ( __CFA_SIGPARMS__ );
    4646static void sigHandler_ill      ( __CFA_SIGPARMS__ );
     
    5656#elif defined( __x86_64 )
    5757#define CFA_REG_IP gregs[REG_RIP]
    58 #elif defined( __ARM_ARCH )
     58#elif defined( __arm__ )
    5959#define CFA_REG_IP arm_pc
     60#elif defined( __aarch64__ )
     61#define CFA_REG_IP pc
    6062#else
    61 #error unknown hardware architecture
     63#error unsupported hardware architecture
    6264#endif
    6365
     
    8385// Get next expired node
    8486static inline alarm_node_t * get_expired( alarm_list_t * alarms, Time currtime ) {
    85         if( !alarms->head ) return 0p;                                          // If no alarms return null
    86         if( alarms->head->alarm >= currtime ) return 0p;        // If alarms head not expired return null
     87        if( ! & (*alarms)`first ) return 0p;                                            // If no alarms return null
     88        if( (*alarms)`first.alarm >= currtime ) return 0p;      // If alarms head not expired return null
    8789        return pop(alarms);                                                                     // Otherwise just pop head
    8890}
    8991
    9092// Tick one frame of the Discrete Event Simulation for alarms
    91 static void tick_preemption() {
     93static void tick_preemption(void) {
    9294        alarm_node_t * node = 0p;                                                       // Used in the while loop but cannot be declared in the while condition
    9395        alarm_list_t * alarms = &event_kernel->alarms;          // Local copy for ease of reading
     
    9799        while( node = get_expired( alarms, currtime ) ) {
    98100                // __cfaabi_dbg_print_buffer_decl( " KERNEL: preemption tick.\n" );
     101                Duration period = node->period;
     102                if( period == 0) {
     103                        node->set = false;                  // Node is one-shot, just mark it as not pending
     104                }
    99105
    100106                // Check if this is a kernel
    101                 if( node->kernel_alarm ) {
     107                if( node->type == Kernel ) {
    102108                        preempt( node->proc );
    103109                }
     110                else if( node->type == User ) {
     111                        timeout( node->thrd );
     112                }
    104113                else {
    105                         timeout( node->thrd );
     114                        node->callback(*node);
    106115                }
    107116
    108117                // Check if this is a periodic alarm
    109                 Duration period = node->period;
    110118                if( period > 0 ) {
    111119                        // __cfaabi_dbg_print_buffer_local( " KERNEL: alarm period is %lu.\n", period.tv );
     
    113121                        insert( alarms, node );             // Reinsert the node for the next time it triggers
    114122                }
    115                 else {
    116                         node->set = false;                  // Node is one-shot, just mark it as not pending
    117                 }
    118123        }
    119124
    120125        // If there are still alarms pending, reset the timer
    121         if( alarms->head ) {
    122                 __cfaabi_dbg_print_buffer_decl( " KERNEL: @%ju(%ju) resetting alarm to %ju.\n", currtime.tv, __kernel_get_time().tv, (alarms->head->alarm - currtime).tv);
    123                 Duration delta = alarms->head->alarm - currtime;
    124                 Duration caped = max(delta, 50`us);
     126        if( & (*alarms)`first ) {
     127                __cfadbg_print_buffer_decl(preemption, " KERNEL: @%ju(%ju) resetting alarm to %ju.\n", currtime.tv, __kernel_get_time().tv, (alarms->head->alarm - currtime).tv);
     128                Duration delta = (*alarms)`first.alarm - currtime;
     129                Duration capped = max(delta, 50`us);
    125130                // itimerval tim  = { caped };
    126131                // __cfaabi_dbg_print_buffer_local( "    Values are %lu, %lu, %lu %lu.\n", delta.tv, caped.tv, tim.it_value.tv_sec, tim.it_value.tv_usec);
    127132
    128                 __kernel_set_timer( caped );
     133                __kernel_set_timer( capped );
    129134        }
    130135}
     
    158163// Kernel Signal Tools
    159164//=============================================================================================
    160 
    161 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; )
     165// In a user-level threading system, there are handful of thread-local variables where this problem occurs on the ARM.
     166//
     167// For each kernel thread running user-level threads, there is a flag variable to indicate if interrupts are
     168// enabled/disabled for that kernel thread. Therefore, this variable is made thread local.
     169//
     170// For example, this code fragment sets the state of the "interrupt" variable in thread-local memory.
     171//
     172// _Thread_local volatile int interrupts;
     173// int main() {
     174//     interrupts = 0; // disable interrupts }
     175//
     176// which generates the following code on the ARM
     177//
     178// (gdb) disassemble main
     179// Dump of assembler code for function main:
     180//    0x0000000000000610 <+0>:  mrs     x1, tpidr_el0
     181//    0x0000000000000614 <+4>:  mov     w0, #0x0                        // #0
     182//    0x0000000000000618 <+8>:  add     x1, x1, #0x0, lsl #12
     183//    0x000000000000061c <+12>: add     x1, x1, #0x10
     184//    0x0000000000000620 <+16>: str     wzr, [x1]
     185//    0x0000000000000624 <+20>: ret
     186//
     187// The mrs moves a pointer from coprocessor register tpidr_el0 into register x1.  Register w0 is set to 0. The two adds
     188// increase the TLS pointer with the displacement (offset) 0x10, which is the location in the TSL of variable
     189// "interrupts".  Finally, 0 is stored into "interrupts" through the pointer in register x1 that points into the
     190// TSL. Now once x1 has the pointer to the location of the TSL for kernel thread N, it can be be preempted at a
     191// user-level and the user thread is put on the user-level ready-queue. When the preempted thread gets to the front of
     192// the user-level ready-queue it is run on kernel thread M. It now stores 0 into "interrupts" back on kernel thread N,
     193// turning off interrupt on the wrong kernel thread.
     194//
     195// On the x86, the following code is generated for the same code fragment.
     196//
     197// (gdb) disassemble main
     198// Dump of assembler code for function main:
     199//    0x0000000000400420 <+0>:  movl   $0x0,%fs:0xfffffffffffffffc
     200//    0x000000000040042c <+12>: xor    %eax,%eax
     201//    0x000000000040042e <+14>: retq
     202//
     203// and there is base-displacement addressing used to atomically reset variable "interrupts" off of the TSL pointer in
     204// register "fs".
     205//
     206// Hence, the ARM has base-displacement address for the general purpose registers, BUT not to the coprocessor
     207// registers. As a result, generating the address for the write into variable "interrupts" is no longer atomic.
     208//
     209// Note this problem does NOT occur when just using multiple kernel threads because the preemption ALWAYS restarts the
     210// thread on the same kernel thread.
     211//
     212// The obvious question is why does ARM use a coprocessor register to store the TSL pointer given that coprocessor
     213// registers are second-class registers with respect to the instruction set. One possible answer is that they did not
     214// want to dedicate one of the general registers to hold the TLS pointer and there was a free coprocessor register
     215// available.
     216
     217//-----------------------------------------------------------------------------
     218// Some assembly required
     219#define __cfaasm_label(label, when) when: asm volatile goto(".global __cfaasm_" #label "_" #when "\n" "__cfaasm_" #label "_" #when ":":::"memory":when)
     220
     221//----------
     222// special case for preemption since used often
     223bool __preemption_enabled() {
     224        // create a assembler label before
     225        // marked as clobber all to avoid movement
     226        __cfaasm_label(check, before);
     227
     228        // access tls as normal
     229        bool enabled = __cfaabi_tls.preemption_state.enabled;
     230
     231        // create a assembler label after
     232        // marked as clobber all to avoid movement
     233        __cfaasm_label(check, after);
     234        return enabled;
     235}
     236
     237struct asm_region {
     238        void * before;
     239        void * after;
     240};
     241
     242static inline bool __cfaasm_in( void * ip, struct asm_region & region ) {
     243        return ip >= region.before && ip <= region.after;
     244}
     245
     246
     247//----------
     248// Get data from the TLS block
     249// struct asm_region __cfaasm_get;
     250uintptr_t __cfatls_get( unsigned long int offset ) __attribute__((__noinline__)); //no inline to avoid problems
     251uintptr_t __cfatls_get( unsigned long int offset ) {
     252        // create a assembler label before
     253        // marked as clobber all to avoid movement
     254        __cfaasm_label(get, before);
     255
     256        // access tls as normal (except for pointer arithmetic)
     257        uintptr_t val = *(uintptr_t*)((uintptr_t)&__cfaabi_tls + offset);
     258
     259        // create a assembler label after
     260        // marked as clobber all to avoid movement
     261        __cfaasm_label(get, after);
     262        return val;
     263}
    162264
    163265extern "C" {
    164266        // Disable interrupts by incrementing the counter
    165267        void disable_interrupts() {
    166                 with( kernelTLS.preemption_state ) {
     268                // create a assembler label before
     269                // marked as clobber all to avoid movement
     270                __cfaasm_label(dsable, before);
     271
     272                with( __cfaabi_tls.preemption_state ) {
    167273                        #if GCC_VERSION > 50000
    168274                        static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free");
     
    181287                        verify( new_val < 65_000u );              // If this triggers someone is disabling interrupts without enabling them
    182288                }
     289
     290                // create a assembler label after
     291                // marked as clobber all to avoid movement
     292                __cfaasm_label(dsable, after);
     293
    183294        }
    184295
     
    186297        // If counter reaches 0, execute any pending __cfactx_switch
    187298        void enable_interrupts( __cfaabi_dbg_ctx_param ) {
    188                 processor   * proc = kernelTLS.this_processor; // Cache the processor now since interrupts can start happening after the atomic store
    189 
    190                 with( kernelTLS.preemption_state ){
     299                // Cache the processor now since interrupts can start happening after the atomic store
     300                processor   * proc = __cfaabi_tls.this_processor;
     301                /* paranoid */ verify( proc );
     302
     303                with( __cfaabi_tls.preemption_state ){
    191304                        unsigned short prev = disable_count;
    192305                        disable_count -= 1;
    193                         verify( prev != 0u );                     // If this triggers someone is enabled already enabled interruptsverify( prev != 0u );
     306
     307                        // If this triggers someone is enabled already enabled interruptsverify( prev != 0u );
     308                        /* paranoid */ verify( prev != 0u );
    194309
    195310                        // Check if we need to prempt the thread because an interrupt was missed
    196311                        if( prev == 1 ) {
    197312                                #if GCC_VERSION > 50000
    198                                 static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free");
     313                                        static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free");
    199314                                #endif
    200315
     
    220335        // Don't execute any pending __cfactx_switch even if counter reaches 0
    221336        void enable_interrupts_noPoll() {
    222                 unsigned short prev = kernelTLS.preemption_state.disable_count;
    223                 kernelTLS.preemption_state.disable_count -= 1;
    224                 verifyf( prev != 0u, "Incremented from %u\n", prev );                     // If this triggers someone is enabled already enabled interrupts
     337                unsigned short prev = __cfaabi_tls.preemption_state.disable_count;
     338                __cfaabi_tls.preemption_state.disable_count -= 1;
     339                // If this triggers someone is enabled already enabled interrupts
     340                /* paranoid */ verifyf( prev != 0u, "Incremented from %u\n", prev );
    225341                if( prev == 1 ) {
    226342                        #if GCC_VERSION > 50000
    227                         static_assert(__atomic_always_lock_free(sizeof(kernelTLS.preemption_state.enabled), &kernelTLS.preemption_state.enabled), "Must be lock-free");
     343                                static_assert(__atomic_always_lock_free(sizeof(__cfaabi_tls.preemption_state.enabled), &__cfaabi_tls.preemption_state.enabled), "Must be lock-free");
    228344                        #endif
    229345                        // Set enabled flag to true
    230346                        // should be atomic to avoid preemption in the middle of the operation.
    231347                        // use memory order RELAXED since there is no inter-thread on this variable requirements
    232                         __atomic_store_n(&kernelTLS.preemption_state.enabled, true, __ATOMIC_RELAXED);
     348                        __atomic_store_n(&__cfaabi_tls.preemption_state.enabled, true, __ATOMIC_RELAXED);
    233349
    234350                        // Signal the compiler that a fence is needed but only for signal handlers
     
    237353        }
    238354}
     355
     356//-----------------------------------------------------------------------------
     357// Kernel Signal Debug
     358void __cfaabi_check_preemption() {
     359        bool ready = __preemption_enabled();
     360        if(!ready) { abort("Preemption should be ready"); }
     361
     362        __cfaasm_label(debug, before);
     363
     364                sigset_t oldset;
     365                int ret;
     366                ret = pthread_sigmask(0, ( const sigset_t * ) 0p, &oldset);  // workaround trac#208: cast should be unnecessary
     367                if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); }
     368
     369                ret = sigismember(&oldset, SIGUSR1);
     370                if(ret <  0) { abort("ERROR sigismember returned %d", ret); }
     371                if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); }
     372
     373                ret = sigismember(&oldset, SIGALRM);
     374                if(ret <  0) { abort("ERROR sigismember returned %d", ret); }
     375                if(ret == 0) { abort("ERROR SIGALRM is enabled"); }
     376
     377                ret = sigismember(&oldset, SIGTERM);
     378                if(ret <  0) { abort("ERROR sigismember returned %d", ret); }
     379                if(ret == 1) { abort("ERROR SIGTERM is disabled"); }
     380
     381        __cfaasm_label(debug, after);
     382}
     383
     384#ifdef __CFA_WITH_VERIFY__
     385bool __cfaabi_dbg_in_kernel() {
     386        return !__preemption_enabled();
     387}
     388#endif
     389
     390#undef __cfaasm_label
     391
     392//-----------------------------------------------------------------------------
     393// Signal handling
    239394
    240395// sigprocmask wrapper : unblock a single signal
     
    256411
    257412        if ( pthread_sigmask( SIG_BLOCK, &mask, 0p ) == -1 ) {
    258             abort( "internal error, pthread_sigmask" );
     413                abort( "internal error, pthread_sigmask" );
    259414        }
    260415}
     
    268423// reserved for future use
    269424static void timeout( $thread * this ) {
    270         //TODO : implement waking threads
    271 }
     425        unpark( this );
     426}
     427
     428//-----------------------------------------------------------------------------
     429// Some assembly required
     430#if defined( __i386 )
     431        #ifdef __PIC__
     432                #define RELOC_PRELUDE( label ) \
     433                        "calll   .Lcfaasm_prelude_" #label "$pb\n\t" \
     434                        ".Lcfaasm_prelude_" #label "$pb:\n\t" \
     435                        "popl    %%eax\n\t" \
     436                        ".Lcfaasm_prelude_" #label "_end:\n\t" \
     437                        "addl    $_GLOBAL_OFFSET_TABLE_+(.Lcfaasm_prelude_" #label "_end-.Lcfaasm_prelude_" #label "$pb), %%eax\n\t"
     438                #define RELOC_PREFIX ""
     439                #define RELOC_SUFFIX "@GOT(%%eax)"
     440        #else
     441                #define RELOC_PREFIX "$"
     442                #define RELOC_SUFFIX ""
     443        #endif
     444        #define __cfaasm_label( label ) struct asm_region label = \
     445                ({ \
     446                        struct asm_region region; \
     447                        asm( \
     448                                RELOC_PRELUDE( label ) \
     449                                "movl " RELOC_PREFIX "__cfaasm_" #label "_before" RELOC_SUFFIX ", %[vb]\n\t" \
     450                                "movl " RELOC_PREFIX "__cfaasm_" #label "_after"  RELOC_SUFFIX ", %[va]\n\t" \
     451                                 : [vb]"=r"(region.before), [va]"=r"(region.after) \
     452                        ); \
     453                        region; \
     454                });
     455#elif defined( __x86_64 )
     456        #ifdef __PIC__
     457                #define RELOC_PREFIX ""
     458                #define RELOC_SUFFIX "@GOTPCREL(%%rip)"
     459        #else
     460                #define RELOC_PREFIX "$"
     461                #define RELOC_SUFFIX ""
     462        #endif
     463        #define __cfaasm_label( label ) struct asm_region label = \
     464                ({ \
     465                        struct asm_region region; \
     466                        asm( \
     467                                "movq " RELOC_PREFIX "__cfaasm_" #label "_before" RELOC_SUFFIX ", %[vb]\n\t" \
     468                                "movq " RELOC_PREFIX "__cfaasm_" #label "_after"  RELOC_SUFFIX ", %[va]\n\t" \
     469                                 : [vb]"=r"(region.before), [va]"=r"(region.after) \
     470                        ); \
     471                        region; \
     472                });
     473#elif defined( __aarch64__ )
     474        #ifdef __PIC__
     475                // Note that this works only for gcc
     476                #define __cfaasm_label( label ) struct asm_region label = \
     477                ({ \
     478                        struct asm_region region; \
     479                        asm( \
     480                                "adrp %[vb], _GLOBAL_OFFSET_TABLE_"                              "\n\t" \
     481                                "ldr  %[vb], [%[vb], #:gotpage_lo15:__cfaasm_" #label "_before]" "\n\t" \
     482                                "adrp %[va], _GLOBAL_OFFSET_TABLE_"                              "\n\t" \
     483                                "ldr  %[va], [%[va], #:gotpage_lo15:__cfaasm_" #label "_after]"  "\n\t" \
     484                                 : [vb]"=r"(region.before), [va]"=r"(region.after) \
     485                        ); \
     486                        region; \
     487                });
     488        #else
     489                #error this is not the right thing to do
     490                /*
     491                #define __cfaasm_label( label ) struct asm_region label = \
     492                ({ \
     493                        struct asm_region region; \
     494                        asm( \
     495                                "adrp %[vb], __cfaasm_" #label "_before"              "\n\t" \
     496                                "add  %[vb], %[vb], :lo12:__cfaasm_" #label "_before" "\n\t" \
     497                                "adrp %[va], :got:__cfaasm_" #label "_after"          "\n\t" \
     498                                "add  %[va], %[va], :lo12:__cfaasm_" #label "_after"  "\n\t" \
     499                                 : [vb]"=r"(region.before), [va]"=r"(region.after) \
     500                        ); \
     501                        region; \
     502                });
     503                */
     504        #endif
     505#else
     506        #error unknown hardware architecture
     507#endif
    272508
    273509// KERNEL ONLY
     
    275511// If true  : preemption is safe
    276512// If false : preemption is unsafe and marked as pending
    277 static inline bool preemption_ready() {
     513static inline bool preemption_ready( void * ip ) {
     514        // Get all the region for which it is not safe to preempt
     515        __cfaasm_label( get    );
     516        __cfaasm_label( check  );
     517        __cfaasm_label( dsable );
     518        __cfaasm_label( debug  );
     519
    278520        // Check if preemption is safe
    279         bool ready = kernelTLS.preemption_state.enabled && ! kernelTLS.preemption_state.in_progress;
    280 
     521        bool ready = true;
     522        if( __cfaasm_in( ip, get    ) ) { ready = false; goto EXIT; };
     523        if( __cfaasm_in( ip, check  ) ) { ready = false; goto EXIT; };
     524        if( __cfaasm_in( ip, dsable ) ) { ready = false; goto EXIT; };
     525        if( __cfaasm_in( ip, debug  ) ) { ready = false; goto EXIT; };
     526        if( !__cfaabi_tls.preemption_state.enabled) { ready = false; goto EXIT; };
     527        if( __cfaabi_tls.preemption_state.in_progress ) { ready = false; goto EXIT; };
     528
     529EXIT:
    281530        // Adjust the pending flag accordingly
    282         kernelTLS.this_processor->pending_preemption = !ready;
     531        __cfaabi_tls.this_processor->pending_preemption = !ready;
    283532        return ready;
    284533}
     
    290539// Startup routine to activate preemption
    291540// Called from kernel_startup
    292 void kernel_start_preemption() {
     541void __kernel_alarm_startup() {
    293542        __cfaabi_dbg_print_safe( "Kernel : Starting preemption\n" );
    294543
    295544        // Start with preemption disabled until ready
    296         kernelTLS.preemption_state.enabled = false;
    297         kernelTLS.preemption_state.disable_count = 1;
     545        __cfaabi_tls.preemption_state.enabled = false;
     546        __cfaabi_tls.preemption_state.disable_count = 1;
    298547
    299548        // Initialize the event kernel
     
    303552        // Setup proper signal handlers
    304553        __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO | SA_RESTART ); // __cfactx_switch handler
     554        __cfaabi_sigaction( SIGALRM, sigHandler_alarm    , SA_SIGINFO | SA_RESTART ); // debug handler
    305555
    306556        signal_block( SIGALRM );
     
    311561// Shutdown routine to deactivate preemption
    312562// Called from kernel_shutdown
    313 void kernel_stop_preemption() {
     563void __kernel_alarm_shutdown() {
    314564        __cfaabi_dbg_print_safe( "Kernel : Preemption stopping\n" );
    315565
     
    325575        // Wait for the preemption thread to finish
    326576
    327         pthread_join( alarm_thread, 0p );
    328         free( alarm_stack );
     577        __destroy_pthread( alarm_thread, alarm_stack, 0p );
    329578
    330579        // Preemption is now fully stopped
     
    352601// Kernel Signal Handlers
    353602//=============================================================================================
     603__cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; )
    354604
    355605// Context switch signal handler
    356606// Receives SIGUSR1 signal and causes the current thread to yield
    357607static void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) {
    358         __cfaabi_dbg_debug_do( last_interrupt = (void *)(cxt->uc_mcontext.CFA_REG_IP); )
     608        void * ip = (void *)(cxt->uc_mcontext.CFA_REG_IP);
     609        __cfaabi_dbg_debug_do( last_interrupt = ip; )
    359610
    360611        // SKULLDUGGERY: if a thread creates a processor and the immediately deletes it,
    361612        // the interrupt that is supposed to force the kernel thread to preempt might arrive
    362         // before the kernel thread has even started running. When that happens an iterrupt
    363         // we a null 'this_processor' will be caught, just ignore it.
    364         if(! kernelTLS.this_processor ) return;
     613        // before the kernel thread has even started running. When that happens, an interrupt
     614        // with a null 'this_processor' will be caught, just ignore it.
     615        if(! __cfaabi_tls.this_processor ) return;
    365616
    366617        choose(sfp->si_value.sival_int) {
    367618                case PREEMPT_NORMAL   : ;// Normal case, nothing to do here
    368                 case PREEMPT_TERMINATE: verify( __atomic_load_n( &kernelTLS.this_processor->do_terminate, __ATOMIC_SEQ_CST ) );
     619                case PREEMPT_TERMINATE: verify( __atomic_load_n( &__cfaabi_tls.this_processor->do_terminate, __ATOMIC_SEQ_CST ) );
    369620                default:
    370621                        abort( "internal error, signal value is %d", sfp->si_value.sival_int );
     
    372623
    373624        // Check if it is safe to preempt here
    374         if( !preemption_ready() ) { return; }
    375 
    376         __cfaabi_dbg_print_buffer_decl( " KERNEL: preempting core %p (%p @ %p).\n", kernelTLS.this_processor, kernelTLS.this_thread, (void *)(cxt->uc_mcontext.CFA_REG_IP) );
     625        if( !preemption_ready( ip ) ) { return; }
     626
     627        __cfaabi_dbg_print_buffer_decl( " KERNEL: preempting core %p (%p @ %p).\n", __cfaabi_tls.this_processor, __cfaabi_tls.this_thread, (void *)(cxt->uc_mcontext.CFA_REG_IP) );
    377628
    378629        // Sync flag : prevent recursive calls to the signal handler
    379         kernelTLS.preemption_state.in_progress = true;
     630        __cfaabi_tls.preemption_state.in_progress = true;
    380631
    381632        // Clear sighandler mask before context switching.
     
    387638        }
    388639
    389         // TODO: this should go in finish action
    390640        // Clear the in progress flag
    391         kernelTLS.preemption_state.in_progress = false;
     641        __cfaabi_tls.preemption_state.in_progress = false;
    392642
    393643        // Preemption can occur here
     
    395645        force_yield( __ALARM_PREEMPTION ); // Do the actual __cfactx_switch
    396646}
     647
     648static void sigHandler_alarm( __CFA_SIGPARMS__ ) {
     649        abort("SIGALRM should never reach the signal handler");
     650}
     651
     652#if !defined(__CFA_NO_STATISTICS__)
     653        int __print_alarm_stats = 0;
     654#endif
    397655
    398656// Main of the alarm thread
    399657// Waits on SIGALRM and send SIGUSR1 to whom ever needs it
    400658static void * alarm_loop( __attribute__((unused)) void * args ) {
     659        __processor_id_t id;
     660        id.full_proc = false;
     661        id.id = doregister(&id);
     662        __cfaabi_tls.this_proc_id = &id;
     663
     664        #if !defined(__CFA_NO_STATISTICS__)
     665                struct __stats_t local_stats;
     666                __cfaabi_tls.this_stats = &local_stats;
     667                __init_stats( &local_stats );
     668        #endif
     669
    401670        // Block sigalrms to control when they arrive
    402671        sigset_t mask;
     
    456725EXIT:
    457726        __cfaabi_dbg_print_safe( "Kernel : Preemption thread stopping\n" );
     727        unregister(&id);
     728
     729        #if !defined(__CFA_NO_STATISTICS__)
     730                if( 0 != __print_alarm_stats ) {
     731                        __print_stats( &local_stats, __print_alarm_stats, "Alarm", "Thread", 0p );
     732                }
     733        #endif
    458734        return 0p;
    459735}
    460 
    461 //=============================================================================================
    462 // Kernel Signal Debug
    463 //=============================================================================================
    464 
    465 void __cfaabi_check_preemption() {
    466         bool ready = kernelTLS.preemption_state.enabled;
    467         if(!ready) { abort("Preemption should be ready"); }
    468 
    469         sigset_t oldset;
    470         int ret;
    471         ret = pthread_sigmask(0, 0p, &oldset);
    472         if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); }
    473 
    474         ret = sigismember(&oldset, SIGUSR1);
    475         if(ret <  0) { abort("ERROR sigismember returned %d", ret); }
    476         if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); }
    477 
    478         ret = sigismember(&oldset, SIGALRM);
    479         if(ret <  0) { abort("ERROR sigismember returned %d", ret); }
    480         if(ret == 0) { abort("ERROR SIGALRM is enabled"); }
    481 
    482         ret = sigismember(&oldset, SIGTERM);
    483         if(ret <  0) { abort("ERROR sigismember returned %d", ret); }
    484         if(ret == 1) { abort("ERROR SIGTERM is disabled"); }
    485 }
    486 
    487 #ifdef __CFA_WITH_VERIFY__
    488 bool __cfaabi_dbg_in_kernel() {
    489         return !kernelTLS.preemption_state.enabled;
    490 }
    491 #endif
    492736
    493737// Local Variables: //
  • libcfa/src/concurrency/preemption.hfa

    r3c64c668 r58fe85a  
    1616#pragma once
    1717
     18#include "bits/locks.hfa"
    1819#include "alarm.hfa"
    19 #include "kernel_private.hfa"
    2020
    21 void kernel_start_preemption();
    22 void kernel_stop_preemption();
     21struct event_kernel_t {
     22        alarm_list_t alarms;
     23        __spinlock_t lock;
     24};
     25
     26extern event_kernel_t * event_kernel;
     27
    2328void update_preemption( processor * this, Duration duration );
    2429
  • libcfa/src/concurrency/thread.cfa

    r3c64c668 r58fe85a  
    1919
    2020#include "kernel_private.hfa"
     21#include "exception.hfa"
    2122
    2223#define __CFA_INVOKE_PRIVATE__
     
    2829        context{ 0p, 0p };
    2930        self_cor{ name, storage, storageSize };
     31        ticket = TICKET_RUNNING;
    3032        state = Start;
    3133        preempted = __NO_PREEMPTION;
     
    3537        self_mon_p = &self_mon;
    3638        curr_cluster = &cl;
    37         next = 0p;
     39        link.next = 0p;
     40        link.prev = 0p;
     41        link.preferred = -1;
     42        #if defined( __CFA_WITH_VERIFY__ )
     43                canary = 0x0D15EA5E0D15EA5Ep;
     44        #endif
     45
     46        seqable.next = 0p;
     47        seqable.back = 0p;
    3848
    3949        node.next = 0p;
     
    4555
    4656void ^?{}($thread& this) with( this ) {
     57        #if defined( __CFA_WITH_VERIFY__ )
     58                canary = 0xDEADDEADDEADDEADp;
     59        #endif
    4760        unregister(curr_cluster, this);
    4861        ^self_cor{};
     62}
     63
     64FORALL_DATA_INSTANCE(ThreadCancelled, (dtype thread_t), (thread_t))
     65
     66forall(dtype T)
     67void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src) {
     68        dst->virtual_table = src->virtual_table;
     69        dst->the_thread = src->the_thread;
     70        dst->the_exception = src->the_exception;
     71}
     72
     73forall(dtype T)
     74const char * msg(ThreadCancelled(T) *) {
     75        return "ThreadCancelled";
     76}
     77
     78forall(dtype T)
     79static void default_thread_cancel_handler(ThreadCancelled(T) & ) {
     80        abort( "Unhandled thread cancellation.\n" );
     81}
     82
     83forall(dtype T | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)))
     84void ?{}( thread_dtor_guard_t & this,
     85                T & thrd, void(*defaultResumptionHandler)(ThreadCancelled(T) &)) {
     86        $monitor * m = get_monitor(thrd);
     87        $thread * desc = get_thread(thrd);
     88
     89        // Setup the monitor guard
     90        void (*dtor)(T& mutex this) = ^?{};
     91        bool join = defaultResumptionHandler != (void(*)(ThreadCancelled(T)&))0;
     92        (this.mg){&m, (void(*)())dtor, join};
     93
     94
     95        /* paranoid */ verifyf( Halted == desc->state || Cancelled == desc->state, "Expected thread to be Halted or Cancelled, was %d\n", (int)desc->state );
     96
     97        // After the guard set-up and any wait, check for cancellation.
     98        struct _Unwind_Exception * cancellation = desc->self_cor.cancellation;
     99        if ( likely( 0p == cancellation ) ) {
     100                return;
     101        } else if ( Cancelled == desc->state ) {
     102                return;
     103        }
     104        desc->state = Cancelled;
     105        if (!join) {
     106                defaultResumptionHandler = default_thread_cancel_handler;
     107        }
     108
     109        ThreadCancelled(T) except;
     110        // TODO: Remove explitate vtable set once trac#186 is fixed.
     111        except.virtual_table = &get_exception_vtable(&except);
     112        except.the_thread = &thrd;
     113        except.the_exception = __cfaehm_cancellation_exception( cancellation );
     114        throwResume except;
     115
     116        except.the_exception->virtual_table->free( except.the_exception );
     117        free( cancellation );
     118        desc->self_cor.cancellation = 0p;
     119}
     120
     121void ^?{}( thread_dtor_guard_t & this ) {
     122        ^(this.mg){};
    49123}
    50124
     
    59133
    60134        this_thrd->context.[SP, FP] = this_thrd->self_cor.context.[SP, FP];
    61         verify( this_thrd->context.SP );
     135        /* paranoid */ verify( this_thrd->context.SP );
    62136
    63         __schedule_thread(this_thrd);
     137        __schedule_thread( this_thrd );
    64138        enable_interrupts( __cfaabi_dbg_ctx );
    65139}
     
    84158}
    85159
     160//-----------------------------------------------------------------------------
     161forall(dtype T | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)))
     162T & join( T & this ) {
     163        thread_dtor_guard_t guard = { this, defaultResumptionHandler };
     164        return this;
     165}
     166
     167uint64_t thread_rand() {
     168        disable_interrupts();
     169        uint64_t ret = __tls_rand();
     170        enable_interrupts( __cfaabi_dbg_ctx );
     171        return ret;
     172}
     173
    86174// Local Variables: //
    87175// mode: c //
  • libcfa/src/concurrency/thread.hfa

    r3c64c668 r58fe85a  
    2222#include "kernel.hfa"
    2323#include "monitor.hfa"
     24#include "exception.hfa"
    2425
    2526//-----------------------------------------------------------------------------
    2627// thread trait
    2728trait is_thread(dtype T) {
    28       void ^?{}(T& mutex this);
    29       void main(T& this);
    30       $thread* get_thread(T& this);
     29        void ^?{}(T& mutex this);
     30        void main(T& this);
     31        $thread* get_thread(T& this);
    3132};
     33
     34FORALL_DATA_EXCEPTION(ThreadCancelled, (dtype thread_t), (thread_t)) (
     35        thread_t * the_thread;
     36        exception_t * the_exception;
     37);
     38
     39forall(dtype T)
     40void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src);
     41
     42forall(dtype T)
     43const char * msg(ThreadCancelled(T) *);
    3244
    3345// define that satisfies the trait without using the thread keyword
     
    6678static inline void ?{}($thread & this, const char * const name, struct cluster & cl, size_t stackSize ) { this{ name, cl, 0p, stackSize }; }
    6779
     80struct thread_dtor_guard_t {
     81        monitor_dtor_guard_t mg;
     82};
     83
     84forall( dtype T | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) )
     85void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(ThreadCancelled(T) &) );
     86void ^?{}( thread_dtor_guard_t & this );
     87
    6888//-----------------------------------------------------------------------------
    6989// thread runner
     
    82102forall( dtype T | sized(T) | is_thread(T) )
    83103void ^?{}( scoped(T)& this );
    84 
    85 //-----------------------------------------------------------------------------
    86 // Thread getters
    87 static inline struct $thread * active_thread () { return TL_GET( this_thread ); }
    88104
    89105//-----------------------------------------------------------------------------
     
    106122bool force_yield( enum __Preemption_Reason );
    107123
    108 static inline void yield() {
    109         force_yield(__MANUAL_PREEMPTION);
    110 }
     124//----------
     125// sleep: force thread to block and be rescheduled after Duration duration
     126void sleep( Duration duration );
    111127
    112 // Yield: yield N times
    113 static inline void yield( unsigned times ) {
    114         for( times ) {
    115                 yield();
    116         }
    117 }
     128//----------
     129// join
     130forall( dtype T | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) )
     131T & join( T & this );
    118132
    119133// Local Variables: //
  • libcfa/src/containers/vector.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Jul  5 18:00:07 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Jul 22 10:01:18 2017
    13 // Update Count     : 3
     12// Last Modified On : Wed Jun 17 11:02:46 2020
     13// Update Count     : 4
    1414//
    1515
    1616#pragma once
    1717
    18 extern "C" {
    1918#include <stdbool.h>
    20 }
    2119
    2220//------------------------------------------------------------------------------
  • libcfa/src/exception.c

    r3c64c668 r58fe85a  
    99// Author           : Andrew Beach
    1010// Created On       : Mon Jun 26 15:13:00 2017
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Feb 22 18:17:34 2018
    13 // Update Count     : 11
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Oct 27 16:27:00 2020
     13// Update Count     : 35
    1414//
    1515
     16// Normally we would get this from the CFA prelude.
    1617#include <stddef.h> // for size_t
    1718
     19#include <unwind.h> // for struct _Unwind_Exception {...};
     20
    1821#include "exception.h"
    19 
    20 // Implementation of the secret header.
    2122
    2223#include <stdlib.h>
    2324#include <stdio.h>
    24 #include <unwind.h>
    2525#include <bits/debug.hfa>
    26 
    27 // FIX ME: temporary hack to keep ARM build working
     26#include "concurrency/invoke.h"
     27#include "stdhdr/assert.h"
     28
     29#if defined( __ARM_ARCH )
     30#warning FIX ME: temporary hack to keep ARM build working
    2831#ifndef _URC_FATAL_PHASE1_ERROR
    29 #define _URC_FATAL_PHASE1_ERROR 2
     32#define _URC_FATAL_PHASE1_ERROR 3
    3033#endif // ! _URC_FATAL_PHASE1_ERROR
    3134#ifndef _URC_FATAL_PHASE2_ERROR
    3235#define _URC_FATAL_PHASE2_ERROR 2
    3336#endif // ! _URC_FATAL_PHASE2_ERROR
     37#endif // __ARM_ARCH
    3438
    3539#include "lsda.h"
    3640
     41/* The exception class for our exceptions. Because of the vendor component
     42 * its value would not be standard.
     43 * Vendor: UWPL
     44 * Language: CFA\0
     45 */
     46const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643;
    3747
    3848// Base exception vtable is abstract, you should not have base exceptions.
    39 struct __cfaabi_ehm__base_exception_t_vtable
    40                 ___cfaabi_ehm__base_exception_t_vtable_instance = {
     49struct __cfaehm_base_exception_t_vtable
     50                ___cfaehm_base_exception_t_vtable_instance = {
    4151        .parent = NULL,
    4252        .size = 0,
     
    4757
    4858
    49 // Temperary global exception context. Does not work with concurency.
    50 struct exception_context_t {
    51     struct __cfaabi_ehm__try_resume_node * top_resume;
    52     struct __cfaabi_ehm__try_resume_node * current_resume;
    53 
    54     exception_t * current_exception;
    55     int current_handler_index;
    56 } shared_stack = {NULL, NULL, 0, 0};
    57 
    5859// Get the current exception context.
    5960// There can be a single global until multithreading occurs, then each stack
    60 // needs its own. It will have to be updated to handle that.
    61 struct exception_context_t * this_exception_context() {
     61// needs its own. We get this from libcfathreads (no weak attribute).
     62__attribute__((weak)) struct exception_context_t * this_exception_context() {
     63        static struct exception_context_t shared_stack = {NULL, NULL};
    6264        return &shared_stack;
    6365}
    64 //#define SAVE_EXCEPTION_CONTEXT(to_name)
    65 //struct exception_context_t * to_name = this_exception_context();
    66 //exception * this_exception() {
    67 //    return this_exception_context()->current_exception;
    68 //}
    69 
    70 
    71 // This macro should be the only thing that needs to change across machines.
    72 // Used in the personality function, way down in termination.
    73 // struct _Unwind_Context * -> _Unwind_Reason_Code(*)(exception_t *)
    74 #define MATCHER_FROM_CONTEXT(ptr_to_context) \
    75         (*(_Unwind_Reason_Code(**)(exception_t *))(_Unwind_GetCFA(ptr_to_context) + 8))
    7666
    7767
    7868// RESUMPTION ================================================================
    7969
    80 void __cfaabi_ehm__throw_resume(exception_t * except) {
    81 
    82         __cfaabi_dbg_print_safe("Throwing resumption exception\n");
    83 
    84         struct __cfaabi_ehm__try_resume_node * original_head = shared_stack.current_resume;
    85         struct __cfaabi_ehm__try_resume_node * current =
    86                 (original_head) ? original_head->next : shared_stack.top_resume;
    87 
    88         for ( ; current ; current = current->next) {
    89                 shared_stack.current_resume = current;
    90                 if (current->handler(except)) {
    91                         shared_stack.current_resume = original_head;
    92                         return;
     70static void reset_top_resume(struct __cfaehm_try_resume_node ** store) {
     71        this_exception_context()->top_resume = *store;
     72}
     73
     74void __cfaehm_throw_resume(exception_t * except, void (*defaultHandler)(exception_t *)) {
     75        struct exception_context_t * context = this_exception_context();
     76
     77        __cfadbg_print_safe(exception, "Throwing resumption exception\n");
     78
     79        {
     80                __attribute__((cleanup(reset_top_resume)))
     81                struct __cfaehm_try_resume_node * original_head = context->top_resume;
     82                struct __cfaehm_try_resume_node * current = context->top_resume;
     83
     84                for ( ; current ; current = current->next) {
     85                        context->top_resume = current->next;
     86                        if (current->handler(except)) {
     87                                return;
     88                        }
    9389                }
    94         }
    95 
    96         __cfaabi_dbg_print_safe("Unhandled exception\n");
    97         shared_stack.current_resume = original_head;
    98 
    99         // Fall back to termination:
    100         __cfaabi_ehm__throw_terminate(except);
    101         // TODO: Default handler for resumption.
     90        } // End the search and return to the top of the stack.
     91
     92        // No handler found, fall back to the default operation.
     93        __cfadbg_print_safe(exception, "Unhandled exception\n");
     94        defaultHandler(except);
    10295}
    10396
     
    10699// be added after the node is built but before it is made the top node.
    107100
    108 void __cfaabi_ehm__try_resume_setup(struct __cfaabi_ehm__try_resume_node * node,
     101void __cfaehm_try_resume_setup(struct __cfaehm_try_resume_node * node,
    109102                        _Bool (*handler)(exception_t * except)) {
    110         node->next = shared_stack.top_resume;
     103        struct exception_context_t * context = this_exception_context();
     104        node->next = context->top_resume;
    111105        node->handler = handler;
    112         shared_stack.top_resume = node;
    113 }
    114 
    115 void __cfaabi_ehm__try_resume_cleanup(struct __cfaabi_ehm__try_resume_node * node) {
    116         shared_stack.top_resume = node->next;
    117 }
    118 
    119 
    120 // TERMINATION ===============================================================
    121 
    122 // MEMORY MANAGEMENT (still for integers)
    123 // May have to move to cfa for constructors and destructors (references).
    124 
    125 struct __cfaabi_ehm__node {
    126         struct __cfaabi_ehm__node * next;
    127 };
     106        context->top_resume = node;
     107}
     108
     109void __cfaehm_try_resume_cleanup(struct __cfaehm_try_resume_node * node) {
     110        struct exception_context_t * context = this_exception_context();
     111        context->top_resume = node->next;
     112}
     113
     114
     115// MEMORY MANAGEMENT =========================================================
    128116
    129117#define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node)))
    130 #define EXCEPT_TO_NODE(except) ((struct __cfaabi_ehm__node *)(except) - 1)
     118#define EXCEPT_TO_NODE(except) ((struct __cfaehm_node *)(except) - 1)
     119#define UNWIND_TO_NODE(unwind) ((struct __cfaehm_node *)(unwind))
     120#define NULL_MAP(map, ptr) ((ptr) ? (map(ptr)) : NULL)
     121
     122// How to clean up an exception in various situations.
     123static void __cfaehm_exception_cleanup(
     124                _Unwind_Reason_Code reason,
     125                struct _Unwind_Exception * exception) {
     126        switch (reason) {
     127        case _URC_FOREIGN_EXCEPTION_CAUGHT:
     128                // This one we could clean-up to allow cross-language exceptions.
     129        case _URC_FATAL_PHASE1_ERROR:
     130        case _URC_FATAL_PHASE2_ERROR:
     131        default:
     132                abort();
     133        }
     134}
    131135
    132136// Creates a copy of the indicated exception and sets current_exception to it.
    133 static void __cfaabi_ehm__allocate_exception( exception_t * except ) {
     137static void __cfaehm_allocate_exception( exception_t * except ) {
    134138        struct exception_context_t * context = this_exception_context();
    135139
    136140        // Allocate memory for the exception.
    137         struct __cfaabi_ehm__node * store = malloc(
    138                 sizeof( struct __cfaabi_ehm__node ) + except->virtual_table->size );
     141        struct __cfaehm_node * store = malloc(
     142                sizeof( struct __cfaehm_node ) + except->virtual_table->size );
    139143
    140144        if ( ! store ) {
     
    143147        }
    144148
     149        // Initialize the node:
     150        exception_t * except_store = NODE_TO_EXCEPT(store);
     151        store->unwind_exception.exception_class = __cfaehm_exception_class;
     152        store->unwind_exception.exception_cleanup = __cfaehm_exception_cleanup;
     153        store->handler_index = 0;
     154        except->virtual_table->copy( except_store, except );
     155
    145156        // Add the node to the list:
    146         store->next = EXCEPT_TO_NODE(context->current_exception);
    147         context->current_exception = NODE_TO_EXCEPT(store);
    148 
    149         // Copy the exception to storage.
    150         except->virtual_table->copy( context->current_exception, except );
     157        store->next = NULL_MAP(EXCEPT_TO_NODE, context->current_exception);
     158        context->current_exception = except_store;
    151159}
    152160
    153161// Delete the provided exception, unsetting current_exception if relivant.
    154 static void __cfaabi_ehm__delete_exception( exception_t * except ) {
    155         struct exception_context_t * context = this_exception_context();
    156 
    157         __cfaabi_dbg_print_safe("Deleting Exception\n");
     162static void __cfaehm_delete_exception( exception_t * except ) {
     163        struct exception_context_t * context = this_exception_context();
     164
     165        __cfadbg_print_safe(exception, "Deleting Exception\n");
    158166
    159167        // Remove the exception from the list.
    160         struct __cfaabi_ehm__node * to_free = EXCEPT_TO_NODE(except);
    161         struct __cfaabi_ehm__node * node;
     168        struct __cfaehm_node * to_free = EXCEPT_TO_NODE(except);
     169        struct __cfaehm_node * node;
    162170
    163171        if ( context->current_exception == except ) {
    164172                node = to_free->next;
    165                 context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0;
     173                context->current_exception = NULL_MAP(NODE_TO_EXCEPT, node);
    166174        } else {
    167175                node = EXCEPT_TO_NODE(context->current_exception);
    168176                // It may always be in the first or second position.
    169                 while( to_free != node->next ) {
     177                while ( to_free != node->next ) {
    170178                        node = node->next;
    171179                }
     
    178186}
    179187
    180 // If this isn't a rethrow (*except==0), delete the provided exception.
    181 void __cfaabi_ehm__cleanup_terminate( void * except ) {
    182         if ( *(void**)except ) __cfaabi_ehm__delete_exception( *(exception_t **)except );
    183 }
    184 
    185 
    186 // We need a piece of storage to raise the exception
    187 struct _Unwind_Exception this_exception_storage;
     188// CANCELLATION ==============================================================
    188189
    189190// Function needed by force unwind
     
    192193                int version,
    193194                _Unwind_Action actions,
    194                 _Unwind_Exception_Class exceptionClass,
     195                _Unwind_Exception_Class exception_class,
    195196                struct _Unwind_Exception * unwind_exception,
    196                 struct _Unwind_Context * context,
    197                 void * some_param) {
    198         if( actions & _UA_END_OF_STACK  ) exit(1);
    199         if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON;
    200 
    201         return _URC_FATAL_PHASE2_ERROR;
     197                struct _Unwind_Context * unwind_context,
     198                void * stop_param) {
     199        // Verify actions follow the rules we expect.
     200        verify(actions & _UA_CLEANUP_PHASE);
     201        verify(actions & _UA_FORCE_UNWIND);
     202        verify(!(actions & _UA_SEARCH_PHASE));
     203        verify(!(actions & _UA_HANDLER_FRAME));
     204
     205        if ( actions & _UA_END_OF_STACK ) {
     206                abort();
     207        } else {
     208                return _URC_NO_REASON;
     209        }
     210}
     211
     212__attribute__((weak)) _Unwind_Reason_Code
     213__cfaehm_cancellation_unwind( struct _Unwind_Exception * exception ) {
     214        return _Unwind_ForcedUnwind( exception, _Stop_Fn, (void*)0x22 );
     215}
     216
     217// Cancel the current stack, prefroming approprate clean-up and messaging.
     218void __cfaehm_cancel_stack( exception_t * exception ) {
     219        __cfaehm_allocate_exception( exception );
     220
     221        struct exception_context_t * context = this_exception_context();
     222        struct __cfaehm_node * node = EXCEPT_TO_NODE(context->current_exception);
     223
     224        // Preform clean-up of any extra active exceptions.
     225        while ( node->next ) {
     226                struct __cfaehm_node * to_free = node->next;
     227                node->next = to_free->next;
     228                exception_t * except = NODE_TO_EXCEPT( to_free );
     229                except->virtual_table->free( except );
     230            free( to_free );
     231        }
     232
     233        _Unwind_Reason_Code ret;
     234        ret = __cfaehm_cancellation_unwind( &node->unwind_exception );
     235        printf("UNWIND ERROR %d after force unwind\n", ret);
     236        abort();
     237}
     238
     239
     240// TERMINATION ===============================================================
     241
     242// If this isn't a rethrow (*except==0), delete the provided exception.
     243void __cfaehm_cleanup_terminate( void * except ) {
     244        if ( *(void**)except ) __cfaehm_delete_exception( *(exception_t **)except );
     245}
     246
     247static void __cfaehm_cleanup_default( exception_t ** except ) {
     248        __cfaehm_delete_exception( *except );
     249        *except = NULL;
    202250}
    203251
    204252// The exception that is being thrown must already be stored.
    205 __attribute__((noreturn)) void __cfaabi_ehm__begin_unwind(void) {
    206         if ( ! this_exception_context()->current_exception ) {
     253static void __cfaehm_begin_unwind(void(*defaultHandler)(exception_t *)) {
     254        struct exception_context_t * context = this_exception_context();
     255        if ( NULL == context->current_exception ) {
    207256                printf("UNWIND ERROR missing exception in begin unwind\n");
    208257                abort();
    209258        }
    210 
     259        struct _Unwind_Exception * storage =
     260                &EXCEPT_TO_NODE(context->current_exception)->unwind_exception;
    211261
    212262        // Call stdlibc to raise the exception
    213         _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage );
     263        __cfadbg_print_safe(exception, "Begin unwinding (storage &p, context %p)\n", storage, context);
     264        _Unwind_Reason_Code ret = _Unwind_RaiseException( storage );
    214265
    215266        // If we reach here it means something happened. For resumption to work we need to find a way
     
    220271        // the whole stack.
    221272
    222         if( ret == _URC_END_OF_STACK ) {
    223                 // No proper handler was found. This can be handled in many ways, C++ calls std::terminate.
    224                 // Here we force unwind the stack, basically raising a cancellation.
    225                 printf("Uncaught exception %p\n", &this_exception_storage);
    226 
    227                 ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 );
    228                 printf("UNWIND ERROR %d after force unwind\n", ret);
     273        // We did not simply reach the end of the stack without finding a handler. This is an error.
     274        if ( ret != _URC_END_OF_STACK ) {
     275                printf("UNWIND ERROR %d after raise exception\n", ret);
    229276                abort();
    230277        }
    231278
    232         // We did not simply reach the end of the stack without finding a handler. This is an error.
    233         printf("UNWIND ERROR %d after raise exception\n", ret);
     279        // No handler found, go to the default operation.
     280        __cfadbg_print_safe(exception, "Uncaught exception %p\n", storage);
     281
     282        __attribute__((cleanup(__cfaehm_cleanup_default)))
     283        exception_t * exception = context->current_exception;
     284        defaultHandler( exception );
     285}
     286
     287void __cfaehm_throw_terminate( exception_t * val, void (*defaultHandler)(exception_t *) ) {
     288        __cfadbg_print_safe(exception, "Throwing termination exception\n");
     289
     290        __cfaehm_allocate_exception( val );
     291        __cfaehm_begin_unwind( defaultHandler );
     292}
     293
     294static __attribute__((noreturn)) void __cfaehm_rethrow_adapter( exception_t * except ) {
     295        // TODO: Print some error message.
     296        (void)except;
    234297        abort();
    235298}
    236299
    237 void __cfaabi_ehm__throw_terminate( exception_t * val ) {
    238         __cfaabi_dbg_print_safe("Throwing termination exception\n");
    239 
    240         __cfaabi_ehm__allocate_exception( val );
    241         __cfaabi_ehm__begin_unwind();
    242 }
    243 
    244 void __cfaabi_ehm__rethrow_terminate(void) {
    245         __cfaabi_dbg_print_safe("Rethrowing termination exception\n");
    246 
    247         __cfaabi_ehm__begin_unwind();
    248 }
    249 
    250 #pragma GCC push_options
    251 #pragma GCC optimize("O0")
    252 
     300void __cfaehm_rethrow_terminate(void) {
     301        __cfadbg_print_safe(exception, "Rethrowing termination exception\n");
     302
     303        __cfaehm_begin_unwind( __cfaehm_rethrow_adapter );
     304        abort();
     305}
     306
     307#if defined( __x86_64 ) || defined( __i386 )
    253308// This is our personality routine. For every stack frame annotated with
    254309// ".cfi_personality 0x3,__gcfa_personality_v0" this function will be called twice when unwinding.
    255310//  Once in the search phase and once in the cleanup phase.
    256 _Unwind_Reason_Code __gcfa_personality_v0 (
    257                 int version, _Unwind_Action actions, unsigned long long exceptionClass,
    258                 struct _Unwind_Exception* unwind_exception,
    259                 struct _Unwind_Context* context)
     311_Unwind_Reason_Code __gcfa_personality_v0(
     312                int version,
     313                _Unwind_Action actions,
     314                unsigned long long exception_class,
     315                struct _Unwind_Exception * unwind_exception,
     316                struct _Unwind_Context * unwind_context)
    260317{
    261318
    262         //__cfaabi_dbg_print_safe("CFA: 0x%lx\n", _Unwind_GetCFA(context));
    263         __cfaabi_dbg_print_safe("Personality function (%d, %x, %llu, %p, %p):",
    264                         version, actions, exceptionClass, unwind_exception, context);
    265 
    266         // If we've reached the end of the stack then there is nothing much we can do...
    267         if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK;
    268 
     319        //__cfadbg_print_safe(exception, "CFA: 0x%lx\n", _Unwind_GetCFA(context));
     320        __cfadbg_print_safe(exception, "Personality function (%d, %x, %llu, %p, %p):",
     321                        version, actions, exception_class, unwind_exception, unwind_context);
     322
     323        // Verify that actions follow the rules we expect.
     324        // This function should never be called at the end of the stack.
     325        verify(!(actions & _UA_END_OF_STACK));
     326        // Either only the search phase flag is set or...
    269327        if (actions & _UA_SEARCH_PHASE) {
    270                 __cfaabi_dbg_print_safe(" lookup phase");
    271         }
    272         else if (actions & _UA_CLEANUP_PHASE) {
    273                 __cfaabi_dbg_print_safe(" cleanup phase");
    274         }
    275         // Just in case, probably can't actually happen
    276         else {
    277                 printf(" error\n");
    278                 return _URC_FATAL_PHASE1_ERROR;
     328                verify(actions == _UA_SEARCH_PHASE);
     329                __cfadbg_print_safe(exception, " lookup phase");
     330        // ... we are in clean-up phase.
     331        } else {
     332                verify(actions & _UA_CLEANUP_PHASE);
     333                __cfadbg_print_safe(exception, " cleanup phase");
     334                // We shouldn't be the handler frame during forced unwind.
     335                if (actions & _UA_HANDLER_FRAME) {
     336                        verify(!(actions & _UA_FORCE_UNWIND));
     337                        __cfadbg_print_safe(exception, " (handler frame)");
     338                } else if (actions & _UA_FORCE_UNWIND) {
     339                        __cfadbg_print_safe(exception, " (force unwind)");
     340                }
    279341        }
    280342
    281343        // Get a pointer to the language specific data from which we will read what we need
    282         const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context );
    283 
    284         if( !lsd ) {    //Nothing to do, keep unwinding
     344        const unsigned char * lsd = _Unwind_GetLanguageSpecificData( unwind_context );
     345
     346        if ( !lsd ) {   //Nothing to do, keep unwinding
    285347                printf(" no LSD");
    286348                goto UNWIND;
     
    289351        // Get the instuction pointer and a reading pointer into the exception table
    290352        lsda_header_info lsd_info;
    291         const unsigned char * cur_ptr = parse_lsda_header(context, lsd, &lsd_info);
    292         _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context );
     353        const unsigned char * cur_ptr = parse_lsda_header(unwind_context, lsd, &lsd_info);
     354        _Unwind_Ptr instruction_ptr = _Unwind_GetIP(unwind_context);
     355
     356        struct exception_context_t * context = this_exception_context();
    293357
    294358        // Linearly search the table for stuff to do
    295         while( cur_ptr < lsd_info.action_table ) {
     359        while ( cur_ptr < lsd_info.action_table ) {
    296360                _Unwind_Ptr callsite_start;
    297361                _Unwind_Ptr callsite_len;
     
    306370
    307371                // Have we reach the correct frame info yet?
    308                 if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
     372                if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
    309373#ifdef __CFA_DEBUG_PRINT__
    310374                        void * ls = (void*)lsd_info.Start;
     
    314378                        void * ep = (void*)lsd_info.Start + callsite_start + callsite_len;
    315379                        void * ip = (void*)instruction_ptr;
    316                         __cfaabi_dbg_print_safe("\nfound %p - %p (%p, %p, %p), looking for %p\n",
     380                        __cfadbg_print_safe(exception, "\nfound %p - %p (%p, %p, %p), looking for %p\n",
    317381                                        bp, ep, ls, cs, cl, ip);
    318382#endif // __CFA_DEBUG_PRINT__
     
    321385
    322386                // Have we gone too far?
    323                 if( lsd_info.Start + callsite_start > instruction_ptr ) {
     387                if ( lsd_info.Start + callsite_start > instruction_ptr ) {
    324388                        printf(" gone too far");
    325389                        break;
    326390                }
    327391
    328                 // Something to do?
    329                 if( callsite_landing_pad ) {
    330                         // Which phase are we in
    331                         if (actions & _UA_SEARCH_PHASE) {
    332                                 // In search phase, these means we found a potential handler we must check.
    333 
    334                                 // We have arbitrarily decided that 0 means nothing to do and 1 means there is
    335                                 // a potential handler. This doesn't seem to conflict the gcc default behavior.
    336                                 if (callsite_action != 0) {
    337                                         // Now we want to run some code to see if the handler matches
    338                                         // This is the tricky part where we want to the power to run arbitrary code
    339                                         // However, generating a new exception table entry and try routine every time
    340                                         // is way more expansive than we might like
    341                                         // The information we have is :
    342                                         //  - The GR (Series of registers)
    343                                         //    GR1=GP Global Pointer of frame ref by context
    344                                         //  - The instruction pointer
    345                                         //  - The instruction pointer info (???)
    346                                         //  - The CFA (Canonical Frame Address)
    347                                         //  - The BSP (Probably the base stack pointer)
    348 
    349 
    350                                         // The current apprach uses one exception table entry per try block
    351                                         _uleb128_t imatcher;
    352                                         // Get the relative offset to the {...}?
    353                                         cur_ptr = read_uleb128(cur_ptr, &imatcher);
    354 
    355                                         _Unwind_Reason_Code (*matcher)(exception_t *) =
    356                                                 MATCHER_FROM_CONTEXT(context);
    357                                         int index = matcher(shared_stack.current_exception);
    358                                         _Unwind_Reason_Code ret = (0 == index)
    359                                                 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND;
    360                                         shared_stack.current_handler_index = index;
    361 
    362                                         // Based on the return value, check if we matched the exception
    363                                         if( ret == _URC_HANDLER_FOUND) {
    364                                                 __cfaabi_dbg_print_safe(" handler found\n");
    365                                         } else {
    366                                                 __cfaabi_dbg_print_safe(" no handler\n");
    367                                         }
    368                                         return ret;
     392                // Check for what we must do:
     393                if ( 0 == callsite_landing_pad ) {
     394                        // Nothing to do, move along
     395                        __cfadbg_print_safe(exception, " no landing pad");
     396                } else if (actions & _UA_SEARCH_PHASE) {
     397                        // In search phase, these means we found a potential handler we must check.
     398
     399                        // We have arbitrarily decided that 0 means nothing to do and 1 means there is
     400                        // a potential handler. This doesn't seem to conflict the gcc default behavior.
     401                        if (callsite_action != 0) {
     402                                // Now we want to run some code to see if the handler matches
     403                                // This is the tricky part where we want to the power to run arbitrary code
     404                                // However, generating a new exception table entry and try routine every time
     405                                // is way more expansive than we might like
     406                                // The information we have is :
     407                                //  - The GR (Series of registers)
     408                                //    GR1=GP Global Pointer of frame ref by context
     409                                //  - The instruction pointer
     410                                //  - The instruction pointer info (???)
     411                                //  - The CFA (Canonical Frame Address)
     412                                //  - The BSP (Probably the base stack pointer)
     413
     414                                // The current apprach uses one exception table entry per try block
     415                                _uleb128_t imatcher;
     416                                // Get the relative offset to the {...}?
     417                                cur_ptr = read_uleb128(cur_ptr, &imatcher);
     418
     419                                _Unwind_Word match_pos =
     420#                               if defined( __x86_64 )
     421                                    _Unwind_GetCFA(unwind_context) + 8;
     422#                               elif defined( __i386 )
     423                                    _Unwind_GetCFA(unwind_context) + 24;
     424#                               elif defined( __ARM_ARCH )
     425#                                   warning FIX ME: check if anything needed for ARM
     426                                    42;
     427#                               endif
     428                                int (*matcher)(exception_t *) = *(int(**)(exception_t *))match_pos;
     429
     430                                int index = matcher(context->current_exception);
     431                                _Unwind_Reason_Code ret = (0 == index)
     432                                        ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND;
     433                                UNWIND_TO_NODE(unwind_exception)->handler_index = index;
     434
     435                                // Based on the return value, check if we matched the exception
     436                                if (ret == _URC_HANDLER_FOUND) {
     437                                        __cfadbg_print_safe(exception, " handler found\n");
     438                                } else {
     439                                        // TODO: Continue the search if there is more in the table.
     440                                        __cfadbg_print_safe(exception, " no handler\n");
    369441                                }
    370 
    371                                 // This is only a cleanup handler, ignore it
    372                                 __cfaabi_dbg_print_safe(" no action");
     442                                return ret;
    373443                        }
    374                         else if (actions & _UA_CLEANUP_PHASE) {
    375 
    376                                 if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){
    377                                         // If this is a potential exception handler
    378                                         // but not the one that matched the exception in the seach phase,
    379                                         // just ignore it
    380                                         goto UNWIND;
    381                                 }
    382 
    383                                 // We need to run some clean-up or a handler
    384                                 // These statment do the right thing but I don't know any specifics at all
    385                                 _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception );
    386                                 _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 );
    387 
    388                                 // I assume this sets the instruction pointer to the adress of the landing pad
    389                                 // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT
    390                                 _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)) );
    391 
    392                                 __cfaabi_dbg_print_safe(" action\n");
    393 
    394                                 // Return have some action to run
    395                                 return _URC_INSTALL_CONTEXT;
     444
     445                        // This is only a cleanup handler, ignore it
     446                        __cfadbg_print_safe(exception, " no action");
     447                } else {
     448                        // In clean-up phase, no destructors here but this could be the handler.
     449
     450                        if ( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){
     451                                // If this is a potential exception handler
     452                                // but not the one that matched the exception in the seach phase,
     453                                // just ignore it
     454                                goto UNWIND;
    396455                        }
     456
     457                        // We need to run some clean-up or a handler
     458                        // These statment do the right thing but I don't know any specifics at all
     459                        _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(0),
     460                                (_Unwind_Ptr)unwind_exception );
     461                        _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(1), 0 );
     462
     463                        // I assume this sets the instruction pointer to the adress of the landing pad
     464                        // It doesn't actually set it, it only state the value that needs to be set once we
     465                        // return _URC_INSTALL_CONTEXT
     466                        _Unwind_SetIP( unwind_context, ((lsd_info.LPStart) + (callsite_landing_pad)) );
     467
     468                        __cfadbg_print_safe(exception, " action\n");
     469
     470                        // Return have some action to run
     471                        return _URC_INSTALL_CONTEXT;
    397472                }
    398 
    399                 // Nothing to do, move along
    400                 __cfaabi_dbg_print_safe(" no landing pad");
    401473        }
    402474        // No handling found
    403         __cfaabi_dbg_print_safe(" table end reached\n");
     475        __cfadbg_print_safe(exception, " table end reached");
    404476
    405477        UNWIND:
    406         __cfaabi_dbg_print_safe(" unwind\n");
     478        __cfadbg_print_safe(exception, " unwind\n");
    407479
    408480        // Keep unwinding the stack
    409481        return _URC_CONTINUE_UNWIND;
    410482}
     483
     484#pragma GCC push_options
     485#pragma GCC optimize(0)
    411486
    412487// Try statements are hoisted out see comments for details. While this could probably be unique
    413488// and simply linked from libcfa but there is one problem left, see the exception table for details
    414489__attribute__((noinline))
    415 void __cfaabi_ehm__try_terminate(void (*try_block)(),
     490void __cfaehm_try_terminate(void (*try_block)(),
    416491                void (*catch_block)(int index, exception_t * except),
    417492                __attribute__((unused)) int (*match_block)(exception_t * except)) {
     
    419494        //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy);
    420495
    421         // Setup statments: These 2 statments won't actually result in any code, they only setup global tables.
    422         // However, they clobber gcc cancellation support from gcc.  We can replace the personality routine but
    423         // replacing the exception table gcc generates is not really doable, it generates labels based on how the
    424         // assembly works.
    425 
    426496        // Setup the personality routine and exception table.
     497        // Unforturnately these clobber gcc cancellation support which means we can't get access to
     498        // the attribute cleanup tables at the same time. We would have to inspect the assembly to
     499        // create a new set ourselves.
    427500#ifdef __PIC__
    428501        asm volatile (".cfi_personality 0x9b,CFA.ref.__gcfa_personality_v0");
     
    449522        // Label which defines the end of the area for which the handler is setup.
    450523        asm volatile (".TRYEND:");
    451         // Label which defines the start of the exception landing pad.  Basically what is called when the exception is
    452         // caught.  Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the
    453         // exception runtime.
     524        // Label which defines the start of the exception landing pad. Basically what is called when
     525        // the exception is caught. Note, if multiple handlers are given, the multiplexing should be
     526        // done by the generated code, not the exception runtime.
    454527        asm volatile (".CATCH:");
    455528
    456529        // Exception handler
    457         catch_block( shared_stack.current_handler_index,
    458                      shared_stack.current_exception );
     530        // Note: Saving the exception context on the stack breaks termination exceptions.
     531        catch_block( EXCEPT_TO_NODE( this_exception_context()->current_exception )->handler_index,
     532                     this_exception_context()->current_exception );
    459533}
    460534
     
    464538
    465539#ifdef __PIC__
    466 #if defined( __i386 ) || defined( __x86_64 )
    467540asm (
    468541        // HEADER
     
    481554        // handler landing pad offset and 1 (action code, gcc seems to use 0).
    482555        ".LLSDACSBCFA2:\n"
    483         "       .uleb128 .TRYSTART-__cfaabi_ehm__try_terminate\n"
     556        "       .uleb128 .TRYSTART-__cfaehm_try_terminate\n"
    484557        "       .uleb128 .TRYEND-.TRYSTART\n"
    485         "       .uleb128 .CATCH-__cfaabi_ehm__try_terminate\n"
     558        "       .uleb128 .CATCH-__cfaehm_try_terminate\n"
    486559        "       .uleb128 1\n"
    487560        ".LLSDACSECFA2:\n"
    488561        // TABLE FOOTER
    489562        "       .text\n"
    490         "       .size   __cfaabi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n"
     563        "       .size   __cfaehm_try_terminate, .-__cfaehm_try_terminate\n"
    491564);
    492565
     
    507580        "       .quad __gcfa_personality_v0\n"
    508581#else // then __i386
    509         "   .long __gcfa_personality_v0\n"
     582        "       .long __gcfa_personality_v0\n"
    510583#endif
    511584);
    512 #else
    513 #error Exception Handling: unknown architecture for position independent code.
    514 #endif // __i386 || __x86_64
    515585#else // __PIC__
    516 #if defined( __i386 ) || defined( __x86_64 )
    517586asm (
    518587        // HEADER
     
    529598        ".LLSDACSBCFA2:\n"
    530599        //      Handled area start (relative to start of function)
    531         "       .uleb128 .TRYSTART-__cfaabi_ehm__try_terminate\n"
     600        "       .uleb128 .TRYSTART-__cfaehm_try_terminate\n"
    532601        //      Handled area length
    533602        "       .uleb128 .TRYEND-.TRYSTART\n"
    534603        //      Handler landing pad address (relative to start of function)
    535         "       .uleb128 .CATCH-__cfaabi_ehm__try_terminate\n"
     604        "       .uleb128 .CATCH-__cfaehm_try_terminate\n"
    536605        //      Action code, gcc seems to always use 0.
    537606        "       .uleb128 1\n"
     
    539608        ".LLSDACSECFA2:\n"
    540609        "       .text\n"
    541         "       .size   __cfaabi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n"
     610        "       .size   __cfaehm_try_terminate, .-__cfaehm_try_terminate\n"
    542611        "       .ident  \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n"
    543612        "       .section        .note.GNU-stack,\"x\",@progbits\n"
    544613);
     614#endif // __PIC__
     615
     616#pragma GCC pop_options
     617
     618#elif defined( __ARM_ARCH )
     619_Unwind_Reason_Code __gcfa_personality_v0(
     620                int version,
     621                _Unwind_Action actions,
     622                unsigned long long exception_class,
     623                struct _Unwind_Exception * unwind_exception,
     624                struct _Unwind_Context * unwind_context) {
     625        return _URC_CONTINUE_UNWIND;
     626}
     627
     628__attribute__((noinline))
     629void __cfaehm_try_terminate(void (*try_block)(),
     630                void (*catch_block)(int index, exception_t * except),
     631                __attribute__((unused)) int (*match_block)(exception_t * except)) {
     632}
    545633#else
    546 #error Exception Handling: unknown architecture for position dependent code.
    547 #endif // __i386 || __x86_64
    548 #endif // __PIC__
    549 
    550 #pragma GCC pop_options
     634        #error unsupported hardware architecture
     635#endif // __x86_64 || __i386
  • libcfa/src/exception.h

    r3c64c668 r58fe85a  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // exception.h -- Builtins for exception handling.
     7// exception.h -- Internal exception handling definitions.
    88//
    99// Author           : Andrew Beach
    1010// Created On       : Mon Jun 26 15:11:00 2017
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Feb 22 18:11:15 2018
    13 // Update Count     : 8
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Oct 27 14:45:00 2020
     13// Update Count     : 11
    1414//
    1515
    1616#pragma once
    1717
     18// This could be considered several headers. All are internal to the exception
     19// system but needed to depending on whether they are C/Cforall code and
     20// whether or not they are part of the builtins.
    1821
    1922#ifdef __cforall
     
    2124#endif
    2225
    23 struct __cfaabi_ehm__base_exception_t;
    24 typedef struct __cfaabi_ehm__base_exception_t exception_t;
    25 struct __cfaabi_ehm__base_exception_t_vtable {
    26         const struct __cfaabi_ehm__base_exception_t_vtable * parent;
     26// Included in C code or the built-ins.
     27#if !defined(__cforall) || defined(__cforall_builtins__)
     28
     29struct __cfaehm_base_exception_t;
     30typedef struct __cfaehm_base_exception_t exception_t;
     31struct __cfaehm_base_exception_t_vtable {
     32        const struct __cfaehm_base_exception_t_vtable * parent;
    2733        size_t size;
    28         void (*copy)(struct __cfaabi_ehm__base_exception_t *this,
    29                      struct __cfaabi_ehm__base_exception_t * other);
    30         void (*free)(struct __cfaabi_ehm__base_exception_t *this);
    31         const char * (*msg)(struct __cfaabi_ehm__base_exception_t *this);
     34        void (*copy)(struct __cfaehm_base_exception_t *this,
     35                     struct __cfaehm_base_exception_t * other);
     36        void (*free)(struct __cfaehm_base_exception_t *this);
     37        const char * (*msg)(struct __cfaehm_base_exception_t *this);
    3238};
    33 struct __cfaabi_ehm__base_exception_t {
    34         struct __cfaabi_ehm__base_exception_t_vtable const * virtual_table;
     39struct __cfaehm_base_exception_t {
     40        struct __cfaehm_base_exception_t_vtable const * virtual_table;
    3541};
    36 extern struct __cfaabi_ehm__base_exception_t_vtable
    37         ___cfaabi_ehm__base_exception_t_vtable_instance;
     42extern struct __cfaehm_base_exception_t_vtable
     43        ___cfaehm_base_exception_t_vtable_instance;
    3844
    3945
     46void __cfaehm_cancel_stack(exception_t * except) __attribute__((noreturn));
     47
    4048// Used in throw statement translation.
    41 void __cfaabi_ehm__throw_terminate(exception_t * except) __attribute__((noreturn));
    42 void __cfaabi_ehm__rethrow_terminate() __attribute__((noreturn));
    43 void __cfaabi_ehm__throw_resume(exception_t * except);
     49void __cfaehm_throw_terminate(exception_t * except, void (*)(exception_t *));
     50void __cfaehm_rethrow_terminate() __attribute__((noreturn));
     51void __cfaehm_throw_resume(exception_t * except, void (*)(exception_t *));
    4452
    4553// Function catches termination exceptions.
    46 void __cfaabi_ehm__try_terminate(
    47     void (*try_block)(),
    48     void (*catch_block)(int index, exception_t * except),
    49     int (*match_block)(exception_t * except));
     54void __cfaehm_try_terminate(
     55        void (*try_block)(),
     56        void (*catch_block)(int index, exception_t * except),
     57        int (*match_block)(exception_t * except));
    5058
    5159// Clean-up the exception in catch blocks.
    52 void __cfaabi_ehm__cleanup_terminate(void * except);
     60void __cfaehm_cleanup_terminate(void * except);
    5361
    5462// Data structure creates a list of resume handlers.
    55 struct __cfaabi_ehm__try_resume_node {
    56     struct __cfaabi_ehm__try_resume_node * next;
    57     _Bool (*handler)(exception_t * except);
     63struct __cfaehm_try_resume_node {
     64        struct __cfaehm_try_resume_node * next;
     65        _Bool (*handler)(exception_t * except);
    5866};
    5967
    6068// These act as constructor and destructor for the resume node.
    61 void __cfaabi_ehm__try_resume_setup(
    62     struct __cfaabi_ehm__try_resume_node * node,
    63     _Bool (*handler)(exception_t * except));
    64 void __cfaabi_ehm__try_resume_cleanup(
    65     struct __cfaabi_ehm__try_resume_node * node);
     69void __cfaehm_try_resume_setup(
     70        struct __cfaehm_try_resume_node * node,
     71        _Bool (*handler)(exception_t * except));
     72void __cfaehm_try_resume_cleanup(
     73        struct __cfaehm_try_resume_node * node);
    6674
    6775// Check for a standard way to call fake deconstructors.
    68 struct __cfaabi_ehm__cleanup_hook {};
     76struct __cfaehm_cleanup_hook {};
     77
     78#endif
     79
     80// Included in C code and the library.
     81#if !defined(__cforall) || !defined(__cforall_builtins__)
     82struct __cfaehm_node {
     83        struct _Unwind_Exception unwind_exception;
     84        struct __cfaehm_node * next;
     85        int handler_index;
     86};
     87
     88static inline exception_t * __cfaehm_cancellation_exception(
     89                struct _Unwind_Exception * unwind_exception ) {
     90        return (exception_t *)(1 + (struct __cfaehm_node *)unwind_exception);
     91}
     92#endif
    6993
    7094#ifdef __cforall
    7195}
     96
     97// Built-ins not visible in C.
     98#if defined(__cforall_builtins__)
     99
     100// Not all the built-ins can be expressed in C. These can't be
     101// implemented in the .c file either so they all have to be inline.
     102
     103trait is_exception(dtype exceptT, dtype virtualT) {
     104        /* The first field must be a pointer to a virtual table.
     105         * That virtual table must be a decendent of the base exception virtual table.
     106         */
     107        virtualT const & get_exception_vtable(exceptT *);
     108        // Always returns the virtual table for this type (associated types hack).
     109};
     110
     111trait is_termination_exception(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT)) {
     112        void defaultTerminationHandler(exceptT &);
     113};
     114
     115trait is_resumption_exception(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT)) {
     116        void defaultResumptionHandler(exceptT &);
     117};
     118
     119forall(dtype exceptT, dtype virtualT | is_termination_exception(exceptT, virtualT))
     120static inline void $throw(exceptT & except) {
     121        __cfaehm_throw_terminate(
     122                (exception_t *)&except,
     123                (void(*)(exception_t *))defaultTerminationHandler
     124        );
     125}
     126
     127forall(dtype exceptT, dtype virtualT | is_resumption_exception(exceptT, virtualT))
     128static inline void $throwResume(exceptT & except) {
     129        __cfaehm_throw_resume(
     130                (exception_t *)&except,
     131                (void(*)(exception_t *))defaultResumptionHandler
     132        );
     133}
     134
     135forall(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT))
     136static inline void cancel_stack(exceptT & except) __attribute__((noreturn)) {
     137        __cfaehm_cancel_stack( (exception_t *)&except );
     138}
     139
     140forall(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT))
     141static inline void defaultTerminationHandler(exceptT & except) {
     142        return cancel_stack( except );
     143}
     144
     145forall(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT))
     146static inline void defaultResumptionHandler(exceptT & except) {
     147        throw except;
     148}
     149
    72150#endif
     151
     152#endif
  • libcfa/src/executor.cfa

    r3c64c668 r58fe85a  
    44// buffer.
    55
    6 #include <bits/containers.hfa>
    76#include <thread.hfa>
    8 #include <stdio.h>
     7#include <containers/list.hfa>
    98
    10 forall( dtype T )
    11 monitor Buffer {                                        // unbounded buffer
    12     __queue_t( T ) queue;                               // unbounded list of work requests
    13     condition delay;
    14 }; // Buffer
    15 forall( dtype T | is_node(T) ) {
    16     void insert( Buffer( T ) & mutex buf, T * elem ) with(buf) {
    17         append( queue, elem );                          // insert element into buffer
    18         signal( delay );                                // restart
    19     } // insert
     9forall( dtype T | $dlistable(T, T) ) {
     10        monitor Buffer {                                                                        // unbounded buffer
     11                dlist( T, T ) queue;                                                    // unbounded list of work requests
     12                condition delay;
     13        }; // Buffer
    2014
    21     T * remove( Buffer( T ) & mutex buf ) with(buf) {
    22         if ( queue.head != 0 ) wait( delay );                   // no request to process ? => wait
    23 //      return pop_head( queue );
    24     } // remove
    25 } // distribution
     15        void insert( Buffer(T) & mutex buf, T * elem ) with(buf) {
     16                dlist( T, T ) * qptr = &queue;                                  // workaround https://cforall.uwaterloo.ca/trac/ticket/166
     17                insert_last( *qptr, *elem );                                    // insert element into buffer
     18                signal( delay );                                                                // restart
     19        } // insert
    2620
    27 struct WRequest {                                       // client request, no return
    28     void (* action)( void );
    29     WRequest * next;                                    // intrusive queue field
     21        T * remove( Buffer(T) & mutex buf ) with(buf) {
     22                dlist( T, T ) * qptr = &queue;                                  // workaround https://cforall.uwaterloo.ca/trac/ticket/166
     23                // if ( (*qptr)`is_empty ) wait( delay );                       // no request to process ? => wait
     24          if ( (*qptr)`is_empty ) return 0p;                            // no request to process ? => wait
     25                return &pop_first( *qptr );
     26        } // remove
     27} // forall
     28
     29struct WRequest {                                                                               // client request, no return
     30        void (* action)( void );
     31        DLISTED_MGD_IMPL_IN(WRequest)
    3032}; // WRequest
     33DLISTED_MGD_IMPL_OUT(WRequest)
    3134
    32 WRequest *& get_next( WRequest & this ) { return this.next; }
    33 void ?{}( WRequest & req ) with(req) { action = 0; next = 0; }
    34 void ?{}( WRequest & req, void (* action)( void ) ) with(req) { req.action = action; next = 0; }
     35void ?{}( WRequest & req ) with(req) { action = 0; }
     36void ?{}( WRequest & req, void (* action)( void ) ) with(req) { req.action = action; }
    3537bool stop( WRequest & req ) { return req.action == 0; }
    3638void doit( WRequest & req ) { req.action(); }
    3739
    38 // Each worker has its own work buffer to reduce contention between client and server. Hence, work requests arrive and
    39 // are distributed into buffers in a roughly round-robin order.
     40// Each worker has its own set (when requests buffers > workers) of work buffers to reduce contention between client
     41// and server, where work requests arrive and are distributed into buffers in a roughly round-robin order.
    4042
    4143thread Worker {
    42     Buffer( WRequest ) * requests;
    43     unsigned int start, range;
     44        Buffer(WRequest) * requests;
     45        WRequest * request;
     46        unsigned int start, range;
    4447}; // Worker
    4548
    4649void main( Worker & w ) with(w) {
    47     for ( int i = 0;; i = (i + 1) % range ) {
    48         WRequest * request = remove( requests[i + start] );
    49       if ( ! request ) { yield(); continue; }
    50       if ( stop( *request ) ) break;
    51         doit( *request );
    52         delete( request );
    53     } // for
     50        for ( int i = 0;; i = (i + 1) % range ) {
     51                request = remove( requests[i + start] );
     52          if ( ! request ) { yield(); continue; }
     53          if ( stop( *request ) ) break;
     54                doit( *request );
     55                delete( request );
     56        } // for
    5457} // Worker::main
    5558
    56 void ?{}( Worker & worker, cluster * wc, Buffer( WRequest ) * requests, unsigned int start, unsigned int range ) {
    57     (*get_thread(worker)){ *wc };                       // create on given cluster
    58     worker.[requests, start, range] = [requests, start, range];
     59void ?{}( Worker & worker, cluster * wc, Buffer(WRequest) * requests, unsigned int start, unsigned int range ) {
     60        ((thread &)worker){ *wc };
     61        worker.[requests, request, start, range] = [requests, 0p, start, range];
    5962} // ?{}
    6063
     64WRequest * current_request( Worker & worker ) { return worker.request; }
     65
    6166struct Executor {
    62     cluster * cluster;                                  // if workers execute on separate cluster
    63     processor ** processors;                            // array of virtual processors adding parallelism for workers
    64     Buffer( WRequest ) * requests;                      // list of work requests
    65     Worker ** workers;                                  // array of workers executing work requests
    66     unsigned int nprocessors, nworkers, nmailboxes;     // number of mailboxes/workers/processor tasks
    67     bool sepClus;                                       // use same or separate cluster for executor
     67        cluster * cluster;                                                                      // if workers execute on separate cluster
     68        processor ** processors;                                                        // array of virtual processors adding parallelism for workers
     69        Buffer(WRequest) * requests;                                            // list of work requests
     70        Worker ** workers;                                                                      // array of workers executing work requests
     71        unsigned int nprocessors, nworkers, nrqueues;           // number of processors/threads/request queues
     72        bool sepClus;                                                                           // use same or separate cluster for executor
     73        unsigned int next;                                                                      // demultiplexed across worker buffers
    6874}; // Executor
    6975
    70 static thread_local unsigned int next;                  // demultiplexed across worker buffers
    7176unsigned int tickets( Executor & ex ) with(ex) {
    72     //return uFetchAdd( next, 1 ) % nmailboxes;
    73     return next++ % nmailboxes;                         // no locking, interference randomizes
     77        //return uFetchAdd( next, 1 ) % nrqueues;
     78        return next++ % nrqueues;                                                       // no locking, interference randomizes
    7479} // tickets
    7580
    76 void ?{}( Executor & ex, unsigned int np, unsigned int nw, unsigned int nm, bool sc = false ) with(ex) {
    77     [nprocessors, nworkers, nmailboxes, sepClus] = [np, nw, nm, sc];
    78     assert( nmailboxes >= nworkers );
    79     cluster = sepClus ? new( "Executor" ) : active_cluster();
    80     processors = (processor **)anew( nprocessors );
    81     requests = anew( nmailboxes );
    82     workers = (Worker **)anew( nworkers );
     81void ?{}( Executor & ex, unsigned int np, unsigned int nw, unsigned int nr, bool sc = false ) with(ex) {
     82        [nprocessors, nworkers, nrqueues, sepClus] = [np, nw, nr, sc];
     83        assert( nrqueues >= nworkers );
     84        cluster = sepClus ? new( "Executor" ) : active_cluster();
     85        processors = aalloc( nprocessors );
     86        requests = anew( nrqueues );
     87        workers = aalloc( nworkers );
    8388
    84     for ( i; nprocessors ) {
    85         processors[ i ] = new( *cluster );
    86     } // for
     89        for ( i; nprocessors ) {
     90                processors[i] = new( *cluster );
     91        } // for
    8792
    88     unsigned int reqPerWorker = nmailboxes / nworkers, extras = nmailboxes % nworkers;
    89     for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker + ( i < extras ? 1 : 0 ) ) {
    90         workers[ i ] = new( cluster, requests, step, reqPerWorker + ( i < extras ? 1 : 0 ) );
    91     } // for
     93        unsigned int reqPerWorker = nrqueues / nworkers, extras = nrqueues % nworkers;
     94//      for ( unsigned int i = 0, start = 0, range; i < nworkers; i += 1, start += range ) {
     95    for ( i; nworkers : start; 0u ~ @ ~ range : range; ) {
     96            range = reqPerWorker + ( i < extras ? 1 : 0 );
     97                workers[i] = new( cluster, requests, start, range );
     98        } // for
    9299} // ?{}
    93100
    94101void ?{}( Executor & ex, unsigned int nprocessors, unsigned int nworkers, bool sepClus = false ) {
    95     ex{ nprocessors, nworkers, nworkers, sepClus };
     102        ex{ nprocessors, nworkers, nworkers, sepClus };
    96103}
    97104void ?{}( Executor & ex, unsigned int nprocessors, bool sepClus = false ) {
    98     ex{ nprocessors, nprocessors, nprocessors, sepClus };
     105        ex{ nprocessors, nprocessors, nprocessors, sepClus };
    99106}
    100 void ?{}( Executor & ex ) {                             // special for current cluster
    101     ex{ 0, active_cluster()->nprocessors, false };
     107void ?{}( Executor & ex ) {                                                             // special for current cluster, no processors added
     108        ex{ 0, active_cluster()->nprocessors, false };
    102109}
    103110void ^?{}( Executor & ex ) with(ex) {
    104     // Add one sentinel per worker to stop them. Since in destructor, no new work should be queued.  Cannot combine next
    105     // two loops and only have a single sentinel because workers arrive in arbitrary order, so worker1 may take the
    106     // single sentinel while waiting for worker 0 to end.
     111        // Add one sentinel per worker to stop them. Since in destructor, no new external work should be queued.  Cannot
     112        // combine next two loops and only have a single sentinel because workers arrive in arbitrary order, so worker1 may
     113        // take the single sentinel while waiting for worker 0 to end.
    107114
    108     WRequest sentinel[nworkers];
    109     unsigned int reqPerWorker = nmailboxes / nworkers;
    110     for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker ) {
    111         insert( requests[step], &sentinel[i] );         // force eventually termination
    112     } // for
    113     for ( i; nworkers ) {
    114         delete( workers[ i ] );
    115     } // for
    116     for ( i; nprocessors ) {
    117         delete( processors[ i ] );
    118     } // for
     115        WRequest sentinel[nworkers];
     116        unsigned int reqPerWorker = nrqueues / nworkers;
     117        for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker ) {
     118                insert( requests[step], &sentinel[i] );                 // force eventually termination
     119        } // for
     120        for ( i; nworkers ) {
     121                delete( workers[i] );
     122        } // for
     123        for ( i; nprocessors ) {
     124                delete( processors[i] );
     125        } // for
    119126
    120     delete( workers );
    121     delete( requests );
    122     delete( processors );
    123     if ( sepClus ) { delete( cluster ); }
     127        free( workers );
     128//      adelete( nrqueues, requests );
     129        for ( i; nrqueues ) ^?{}( requests[i] );                        // FIX ME: problem with resolver
     130        free( requests );
     131        free( processors );
     132        if ( sepClus ) { delete( cluster ); }
    124133} // ^?{}
    125134
    126135void send( Executor & ex, void (* action)( void ) ) {   // asynchronous call, no return value
    127     WRequest * node = new( action );
    128     insert( ex.requests[tickets( ex )], node );
     136        WRequest * node = new( action );
     137        insert( ex.requests[tickets( ex )], node );
    129138} // send
     139
    130140
    131141int counter = 0;
    132142
    133 void workie( void ) {
    134     __atomic_add_fetch( &counter, 1, __ATOMIC_SEQ_CST );
    135 //    fprintf( stderr, "workie\n" );
     143void work( void ) {
     144        __atomic_add_fetch( &counter, 1, __ATOMIC_SEQ_CST );
     145        // fprintf( stderr, "workie\n" );
    136146}
    137147
    138 int main() {
    139     {
    140         Executor exector;
    141         for ( i; 3000 ) {
    142             send( exector, workie );
    143             if ( i % 100 ) yield();
    144         } // for
    145     }
    146     printf( "%d\n", counter );
     148int main( int argc, char * argv[] ) {
     149        int times = 1_000_000;
     150        if ( argc == 2 ) times = atoi( argv[1] );
     151        processor p[7];
     152        {
     153                Executor exector;
     154                for ( i; times ) {
     155                        send( exector, work );
     156                        if ( i % 100 == 0 ) yield();
     157                } // for
     158        }
     159        printf( "%d\n", counter );
    147160}
    148161
    149162// Local Variables: //
     163// tab-width: 4" //
    150164// compile-command: "cfa executor.cfa" //
    151165// End: //
  • libcfa/src/fstream.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Feb  7 19:01:01 2020
    13 // Update Count     : 363
     12// Last Modified On : Fri Jun 19 16:24:54 2020
     13// Update Count     : 384
    1414//
    1515
     
    2626
    2727
    28 //*********************************** ofstream ***********************************
     28// *********************************** ofstream ***********************************
    2929
    3030
     
    123123        #ifdef __CFA_DEBUG__
    124124        if ( file == 0p ) {
    125                 abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno );
     125                throw (Open_Failure){ os };
     126                // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno );
    126127        } // if
    127128        #endif // __CFA_DEBUG__
     
    134135
    135136void close( ofstream & os ) {
    136         if ( (FILE *)(os.$file) == stdout || (FILE *)(os.$file) == stderr ) return;
     137  if ( (FILE *)(os.$file) == 0p ) return;
     138  if ( (FILE *)(os.$file) == (FILE *)stdout || (FILE *)(os.$file) == (FILE *)stderr ) return;
    137139
    138140        if ( fclose( (FILE *)(os.$file) ) == EOF ) {
    139141                abort | IO_MSG "close output" | nl | strerror( errno );
    140142        } // if
     143        os.$file = 0p;
    141144} // close
    142145
     
    179182
    180183
    181 //*********************************** ifstream ***********************************
     184// *********************************** ifstream ***********************************
    182185
    183186
     
    219222        #ifdef __CFA_DEBUG__
    220223        if ( file == 0p ) {
    221                 abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno );
     224                throw (Open_Failure){ is };
     225                // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno );
    222226        } // if
    223227        #endif // __CFA_DEBUG__
     
    230234
    231235void close( ifstream & is ) {
    232         if ( (FILE *)(is.$file) == stdin ) return;
     236  if ( (FILE *)(is.$file) == 0p ) return;
     237  if ( (FILE *)(is.$file) == (FILE *)stdin ) return;
    233238
    234239        if ( fclose( (FILE *)(is.$file) ) == EOF ) {
    235240                abort | IO_MSG "close input" | nl | strerror( errno );
    236241        } // if
     242        is.$file = 0p;
    237243} // close
    238244
     
    276282ifstream & sin = sinFile, & stdin = sinFile;
    277283
     284
     285// *********************************** exceptions ***********************************
     286
     287
     288void ?{}( Open_Failure & this, ofstream & ostream ) {
     289        VTABLE_INIT(this, Open_Failure);
     290        this.ostream = &ostream;
     291        this.tag = 1;
     292}
     293void ?{}( Open_Failure & this, ifstream & istream ) {
     294        VTABLE_INIT(this, Open_Failure);
     295        this.istream = &istream;
     296        this.tag = 0;
     297}
     298const char * Open_Failure_msg(Open_Failure * this) {
     299        return "Open_Failure";
     300}
     301VTABLE_INSTANCE(Open_Failure)(Open_Failure_msg);
     302void throwOpen_Failure( ofstream & ostream ) {
     303        Open_Failure exc = { ostream };
     304}
     305void throwOpen_Failure( ifstream & istream ) {
     306        Open_Failure exc = { istream };
     307}
     308
    278309// Local Variables: //
    279310// tab-width: 4 //
  • libcfa/src/fstream.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Feb 17 08:29:23 2020
    13 // Update Count     : 175
     12// Last Modified On : Fri Jun 19 16:29:17 2020
     13// Update Count     : 189
    1414//
    1515
     
    1717
    1818#include "iostream.hfa"
     19#include <exception.hfa>
    1920
    2021
    21 //*********************************** ofstream ***********************************
     22// *********************************** ofstream ***********************************
    2223
    2324
     
    7879
    7980
    80 //*********************************** ifstream ***********************************
     81// *********************************** ifstream ***********************************
    8182
    8283
     
    106107extern ifstream & sin, & stdin;                                                 // aliases
    107108
     109
     110// *********************************** exceptions ***********************************
     111
     112
     113DATA_EXCEPTION(Open_Failure)(
     114        union {
     115                ofstream * ostream;
     116                ifstream * istream;
     117        };
     118        // TEMPORARY: need polymorphic exceptions
     119        int tag;                                                                                        // 1 => ostream; 0 => istream
     120);
     121
     122void ?{}( Open_Failure & this, ofstream & ostream );
     123void ?{}( Open_Failure & this, ifstream & istream );
     124
    108125// Local Variables: //
    109126// mode: c //
  • libcfa/src/heap.cfa

    r3c64c668 r58fe85a  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // heap.c --
     7// heap.cfa --
    88//
    99// Author           : Peter A. Buhr
    1010// Created On       : Tue Dec 19 21:58:35 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 10:04:51 2020
    13 // Update Count     : 648
     12// Last Modified On : Wed Dec 16 12:28:25 2020
     13// Update Count     : 1023
    1414//
    1515
    1616#include <unistd.h>                                                                             // sbrk, sysconf
     17#include <stdlib.h>                                                                             // EXIT_FAILURE
    1718#include <stdbool.h>                                                                    // true, false
    1819#include <stdio.h>                                                                              // snprintf, fileno
    1920#include <errno.h>                                                                              // errno
    2021#include <string.h>                                                                             // memset, memcpy
    21 extern "C" {
     22#include <limits.h>                                                                             // ULONG_MAX
     23#include <malloc.h>                                                                             // memalign, malloc_usable_size
    2224#include <sys/mman.h>                                                                   // mmap, munmap
    23 } // extern "C"
    24 
    25 // #comment TD : Many of these should be merged into math I believe
    26 #include "bits/align.hfa"                                                               // libPow2
     25
     26#include "bits/align.hfa"                                                               // libAlign
    2727#include "bits/defs.hfa"                                                                // likely, unlikely
    2828#include "bits/locks.hfa"                                                               // __spinlock_t
    2929#include "startup.hfa"                                                                  // STARTUP_PRIORITY_MEMORY
    30 //#include "stdlib.hfa"                                                                 // bsearchl
    31 #include "malloc.h"
    32 
    33 #define MIN(x, y) (y > x ? x : y)
     30#include "math.hfa"                                                                             // ceiling
     31#include "bitmanip.hfa"                                                                 // is_pow2, ceiling2
    3432
    3533static bool traceHeap = false;
     
    7472        // Define the default extension heap amount in units of bytes. When the uC++ supplied heap reaches the brk address,
    7573        // the brk address is extended by the extension amount.
    76         __CFA_DEFAULT_HEAP_EXPANSION__ = (1 * 1024 * 1024),
     74        __CFA_DEFAULT_HEAP_EXPANSION__ = (10 * 1024 * 1024),
    7775
    7876        // Define the mmap crossover point during allocation. Allocations less than this amount are allocated from buckets;
     
    9189
    9290#ifdef __CFA_DEBUG__
    93 static unsigned int allocFree;                                                  // running total of allocations minus frees
     91static size_t allocUnfreed;                                                             // running total of allocations minus frees
    9492
    9593static void prtUnfreed() {
    96         if ( allocFree != 0 ) {
     94        if ( allocUnfreed != 0 ) {
    9795                // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.
    9896                char helpText[512];
    99                 int len = snprintf( helpText, sizeof(helpText), "CFA warning (UNIX pid:%ld) : program terminating with %u(0x%x) bytes of storage allocated but not freed.\n"
     97                int len = snprintf( helpText, sizeof(helpText), "CFA warning (UNIX pid:%ld) : program terminating with %zu(0x%zx) bytes of storage allocated but not freed.\n"
    10098                                                        "Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n",
    101                                                         (long int)getpid(), allocFree, allocFree ); // always print the UNIX pid
     99                                                        (long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid
    102100                __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug
    103101        } // if
     
    106104extern "C" {
    107105        void heapAppStart() {                                                           // called by __cfaabi_appready_startup
    108                 allocFree = 0;
     106                allocUnfreed = 0;
    109107        } // heapAppStart
    110108
     
    118116
    119117// statically allocated variables => zero filled.
    120 static size_t pageSize;                                                                 // architecture pagesize
     118size_t __page_size;                                                                             // architecture pagesize
     119int __map_prot;                                                                                 // common mmap/mprotect protection
    121120static size_t heapExpand;                                                               // sbrk advance
    122121static size_t mmapStart;                                                                // cross over point for mmap
     
    127126#define LOCKFREE 1
    128127#define BUCKETLOCK SPINLOCK
    129 #if BUCKETLOCK == LOCKFREE
    130 #include <uStackLF.h>
     128#if BUCKETLOCK == SPINLOCK
     129#elif BUCKETLOCK == LOCKFREE
     130#include <stackLockFree.hfa>
     131#else
     132        #error undefined lock type for bucket lock
    131133#endif // LOCKFREE
    132134
     
    136138
    137139struct HeapManager {
    138 //      struct FreeHeader;                                                                      // forward declaration
    139 
    140140        struct Storage {
    141141                struct Header {                                                                 // header
     
    145145                                                struct {                                                // 4-byte word => 8-byte header, 8-byte word => 16-byte header
    146146                                                        #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ && __SIZEOF_POINTER__ == 4
    147                                                         uint32_t padding;                       // unused, force home/blocksize to overlay alignment in fake header
     147                                                        uint64_t padding;                       // unused, force home/blocksize to overlay alignment in fake header
    148148                                                        #endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ && __SIZEOF_POINTER__ == 4
    149149
    150150                                                        union {
    151 //                                                              FreeHeader * home;              // allocated block points back to home locations (must overlay alignment)
     151                                                                // FreeHeader * home;           // allocated block points back to home locations (must overlay alignment)
     152                                                                // 2nd low-order bit => zero filled
    152153                                                                void * home;                    // allocated block points back to home locations (must overlay alignment)
    153154                                                                size_t blockSize;               // size for munmap (must overlay alignment)
    154                                                                 #if BUCKLOCK == SPINLOCK
     155                                                                #if BUCKETLOCK == SPINLOCK
    155156                                                                Storage * next;                 // freed block points next freed block of same size
    156157                                                                #endif // SPINLOCK
    157158                                                        };
     159                                                        size_t size;                            // allocation size in bytes
    158160
    159161                                                        #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __SIZEOF_POINTER__ == 4
    160                                                         uint32_t padding;                       // unused, force home/blocksize to overlay alignment in fake header
     162                                                        uint64_t padding;                       // unused, force home/blocksize to overlay alignment in fake header
    161163                                                        #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __SIZEOF_POINTER__ == 4
    162164                                                };
    163                                                 // future code
    164                                                 #if BUCKLOCK == LOCKFREE
    165                                                 Stack<Storage>::Link next;              // freed block points next freed block of same size (double-wide)
     165                                                #if BUCKETLOCK == LOCKFREE
     166                                                Link(Storage) next;                             // freed block points next freed block of same size (double-wide)
    166167                                                #endif // LOCKFREE
    167168                                        };
    168169                                } real; // RealHeader
     170
    169171                                struct FakeHeader {
    170172                                        #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    171                                         uint32_t alignment;                                     // low-order bits of home/blockSize used for tricks
     173                                        uint32_t alignment;                                     // 1st low-order bit => fake header & alignment
    172174                                        #endif // __ORDER_LITTLE_ENDIAN__
    173175
     
    187189
    188190        struct FreeHeader {
    189                 #if BUCKLOCK == SPINLOCK
     191                #if BUCKETLOCK == SPINLOCK
    190192                __spinlock_t lock;                                                              // must be first field for alignment
    191193                Storage * freeList;
    192                 #elif BUCKLOCK == LOCKFREE
    193                 // future code
    194                 StackLF<Storage> freeList;
    195194                #else
    196                         #error undefined lock type for bucket lock
    197                 #endif // SPINLOCK
     195                StackLF(Storage) freeList;
     196                #endif // BUCKETLOCK
    198197                size_t blockSize;                                                               // size of allocations on this list
    199198        }; // FreeHeader
     
    208207}; // HeapManager
    209208
     209#if BUCKETLOCK == LOCKFREE
     210static inline {
     211        Link(HeapManager.Storage) * ?`next( HeapManager.Storage * this ) { return &this->header.kind.real.next; }
     212        void ?{}( HeapManager.FreeHeader & ) {}
     213        void ^?{}( HeapManager.FreeHeader & ) {}
     214} // distribution
     215#endif // LOCKFREE
     216
    210217static inline size_t getKey( const HeapManager.FreeHeader & freeheader ) { return freeheader.blockSize; }
    211218
     
    214221#define __STATISTICS__
    215222
    216 // Bucket size must be multiple of 16.
    217 // Powers of 2 are common allocation sizes, so make powers of 2 generate the minimum required size.
     223// Size of array must harmonize with NoBucketSizes and individual bucket sizes must be multiple of 16.
     224// Smaller multiples of 16 and powers of 2 are common allocation sizes, so make them generate the minimum required bucket size.
     225// malloc(0) returns 0p, so no bucket is necessary for 0 bytes returning an address that can be freed.
    218226static const unsigned int bucketSizes[] @= {                    // different bucket sizes
    219         16, 32, 48, 64 + sizeof(HeapManager.Storage), // 4
    220         96, 112, 128 + sizeof(HeapManager.Storage), // 3
     227        16 + sizeof(HeapManager.Storage), 32 + sizeof(HeapManager.Storage), 48 + sizeof(HeapManager.Storage), 64 + sizeof(HeapManager.Storage), // 4
     228        96 + sizeof(HeapManager.Storage), 112 + sizeof(HeapManager.Storage), 128 + sizeof(HeapManager.Storage), // 3
    221229        160, 192, 224, 256 + sizeof(HeapManager.Storage), // 4
    222230        320, 384, 448, 512 + sizeof(HeapManager.Storage), // 4
     
    236244};
    237245
    238 static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0]), "size of bucket array wrong" );
     246static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" );
    239247
    240248#ifdef FASTLOOKUP
     
    243251#endif // FASTLOOKUP
    244252
    245 static int mmapFd = -1;                                                                 // fake or actual fd for anonymous file
     253static const off_t mmapFd = -1;                                                 // fake or actual fd for anonymous file
    246254#ifdef __CFA_DEBUG__
    247255static bool heapBoot = 0;                                                               // detect recursion during boot
    248256#endif // __CFA_DEBUG__
     257
     258// The constructor for heapManager is called explicitly in memory_startup.
    249259static HeapManager heapManager __attribute__(( aligned (128) )) @= {}; // size of cache line to prevent false sharing
    250260
     
    252262#ifdef __STATISTICS__
    253263// Heap statistics counters.
     264static unsigned int malloc_calls;
     265static unsigned long long int malloc_storage;
     266static unsigned int aalloc_calls;
     267static unsigned long long int aalloc_storage;
     268static unsigned int calloc_calls;
     269static unsigned long long int calloc_storage;
     270static unsigned int memalign_calls;
     271static unsigned long long int memalign_storage;
     272static unsigned int amemalign_calls;
     273static unsigned long long int amemalign_storage;
     274static unsigned int cmemalign_calls;
     275static unsigned long long int cmemalign_storage;
     276static unsigned int resize_calls;
     277static unsigned long long int resize_storage;
     278static unsigned int realloc_calls;
     279static unsigned long long int realloc_storage;
     280static unsigned int free_calls;
     281static unsigned long long int free_storage;
     282static unsigned int mmap_calls;
    254283static unsigned long long int mmap_storage;
    255 static unsigned int mmap_calls;
     284static unsigned int munmap_calls;
    256285static unsigned long long int munmap_storage;
    257 static unsigned int munmap_calls;
     286static unsigned int sbrk_calls;
    258287static unsigned long long int sbrk_storage;
    259 static unsigned int sbrk_calls;
    260 static unsigned long long int malloc_storage;
    261 static unsigned int malloc_calls;
    262 static unsigned long long int free_storage;
    263 static unsigned int free_calls;
    264 static unsigned long long int calloc_storage;
    265 static unsigned int calloc_calls;
    266 static unsigned long long int memalign_storage;
    267 static unsigned int memalign_calls;
    268 static unsigned long long int cmemalign_storage;
    269 static unsigned int cmemalign_calls;
    270 static unsigned long long int realloc_storage;
    271 static unsigned int realloc_calls;
    272288// Statistics file descriptor (changed by malloc_stats_fd).
    273 static int statfd = STDERR_FILENO;                                              // default stderr
     289static int stat_fd = STDERR_FILENO;                                             // default stderr
    274290
    275291// Use "write" because streams may be shutdown when calls are made.
    276292static void printStats() {
    277         char helpText[512];
     293        char helpText[1024];
    278294        __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
    279295                                                                        "\nHeap statistics:\n"
    280296                                                                        "  malloc: calls %u / storage %llu\n"
     297                                                                        "  aalloc: calls %u / storage %llu\n"
    281298                                                                        "  calloc: calls %u / storage %llu\n"
    282299                                                                        "  memalign: calls %u / storage %llu\n"
     300                                                                        "  amemalign: calls %u / storage %llu\n"
    283301                                                                        "  cmemalign: calls %u / storage %llu\n"
     302                                                                        "  resize: calls %u / storage %llu\n"
    284303                                                                        "  realloc: calls %u / storage %llu\n"
    285304                                                                        "  free: calls %u / storage %llu\n"
     
    288307                                                                        "  sbrk: calls %u / storage %llu\n",
    289308                                                                        malloc_calls, malloc_storage,
     309                                                                        aalloc_calls, aalloc_storage,
    290310                                                                        calloc_calls, calloc_storage,
    291311                                                                        memalign_calls, memalign_storage,
     312                                                                        amemalign_calls, amemalign_storage,
    292313                                                                        cmemalign_calls, cmemalign_storage,
     314                                                                        resize_calls, resize_storage,
    293315                                                                        realloc_calls, realloc_storage,
    294316                                                                        free_calls, free_storage,
     
    300322
    301323static int printStatsXML( FILE * stream ) {                             // see malloc_info
    302         char helpText[512];
     324        char helpText[1024];
    303325        int len = snprintf( helpText, sizeof(helpText),
    304326                                                "<malloc version=\"1\">\n"
     
    307329                                                "</sizes>\n"
    308330                                                "<total type=\"malloc\" count=\"%u\" size=\"%llu\"/>\n"
     331                                                "<total type=\"aalloc\" count=\"%u\" size=\"%llu\"/>\n"
    309332                                                "<total type=\"calloc\" count=\"%u\" size=\"%llu\"/>\n"
    310333                                                "<total type=\"memalign\" count=\"%u\" size=\"%llu\"/>\n"
     334                                                "<total type=\"amemalign\" count=\"%u\" size=\"%llu\"/>\n"
    311335                                                "<total type=\"cmemalign\" count=\"%u\" size=\"%llu\"/>\n"
     336                                                "<total type=\"resize\" count=\"%u\" size=\"%llu\"/>\n"
    312337                                                "<total type=\"realloc\" count=\"%u\" size=\"%llu\"/>\n"
    313338                                                "<total type=\"free\" count=\"%u\" size=\"%llu\"/>\n"
     
    317342                                                "</malloc>",
    318343                                                malloc_calls, malloc_storage,
     344                                                aalloc_calls, aalloc_storage,
    319345                                                calloc_calls, calloc_storage,
    320346                                                memalign_calls, memalign_storage,
     347                                                amemalign_calls, amemalign_storage,
    321348                                                cmemalign_calls, cmemalign_storage,
     349                                                resize_calls, resize_storage,
    322350                                                realloc_calls, realloc_storage,
    323351                                                free_calls, free_storage,
     
    332360
    333361
    334 // static inline void noMemory() {
    335 //      abort( "Heap memory exhausted at %zu bytes.\n"
    336 //                 "Possible cause is very large memory allocation and/or large amount of unfreed storage allocated by the program or system/library routines.",
    337 //                 ((char *)(sbrk( 0 )) - (char *)(heapManager.heapBegin)) );
    338 // } // noMemory
    339 
    340 
    341 static inline void checkAlign( size_t alignment ) {
    342         if ( alignment < libAlign() || ! libPow2( alignment ) ) {
    343                 abort( "Alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() );
    344         } // if
    345 } // checkAlign
    346 
    347 
    348 static inline bool setHeapExpand( size_t value ) {
    349   if ( heapExpand < pageSize ) return true;
    350         heapExpand = value;
    351         return false;
    352 } // setHeapExpand
    353 
    354 
    355362// thunk problem
    356363size_t Bsearchl( unsigned int key, const unsigned int * vals, size_t dim ) {
     
    369376
    370377static inline bool setMmapStart( size_t value ) {               // true => mmapped, false => sbrk
    371   if ( value < pageSize || bucketSizes[NoBucketSizes - 1] < value ) return true;
     378  if ( value < __page_size || bucketSizes[NoBucketSizes - 1] < value ) return false;
    372379        mmapStart = value;                                                                      // set global
    373380
     
    376383        assert( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
    377384        assert( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
    378         return false;
     385        return true;
    379386} // setMmapStart
     387
     388
     389// <-------+----------------------------------------------------> bsize (bucket size)
     390// |header |addr
     391//==================================================================================
     392//                   align/offset |
     393// <-----------------<------------+-----------------------------> bsize (bucket size)
     394//                   |fake-header | addr
     395#define headerAddr( addr ) ((HeapManager.Storage.Header *)( (char *)addr - sizeof(HeapManager.Storage) ))
     396#define realHeader( header ) ((HeapManager.Storage.Header *)((char *)header - header->kind.fake.offset))
     397
     398// <-------<<--------------------- dsize ---------------------->> bsize (bucket size)
     399// |header |addr
     400//==================================================================================
     401//                   align/offset |
     402// <------------------------------<<---------- dsize --------->>> bsize (bucket size)
     403//                   |fake-header |addr
     404#define dataStorage( bsize, addr, header ) (bsize - ( (char *)addr - (char *)header ))
     405
     406
     407static inline void checkAlign( size_t alignment ) {
     408        if ( alignment < libAlign() || ! is_pow2( alignment ) ) {
     409                abort( "Alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() );
     410        } // if
     411} // checkAlign
    380412
    381413
     
    391423static inline void fakeHeader( HeapManager.Storage.Header *& header, size_t & alignment ) {
    392424        if ( unlikely( (header->kind.fake.alignment & 1) == 1 ) ) { // fake header ?
    393                 size_t offset = header->kind.fake.offset;
    394425                alignment = header->kind.fake.alignment & -2;   // remove flag from value
    395426                #ifdef __CFA_DEBUG__
    396427                checkAlign( alignment );                                                // check alignment
    397428                #endif // __CFA_DEBUG__
    398                 header = (HeapManager.Storage.Header *)((char *)header - offset);
     429                header = realHeader( header );                                  // backup from fake to real header
     430        } else {
     431                alignment = libAlign();                                                 // => no fake header
    399432        } // if
    400433} // fakeHeader
    401434
    402435
    403 // <-------+----------------------------------------------------> bsize (bucket size)
    404 // |header |addr
    405 //==================================================================================
    406 //                                | alignment
    407 // <-----------------<------------+-----------------------------> bsize (bucket size)
    408 //                   |fake-header | addr
    409 #define headerAddr( addr ) ((HeapManager.Storage.Header *)( (char *)addr - sizeof(HeapManager.Storage) ))
    410 
    411 // <-------<<--------------------- dsize ---------------------->> bsize (bucket size)
    412 // |header |addr
    413 //==================================================================================
    414 //                                | alignment
    415 // <------------------------------<<---------- dsize --------->>> bsize (bucket size)
    416 //                   |fake-header |addr
    417 #define dataStorage( bsize, addr, header ) (bsize - ( (char *)addr - (char *)header ))
    418 
    419 
    420 static inline bool headers( const char name[] __attribute__(( unused )), void * addr, HeapManager.Storage.Header *& header, HeapManager.FreeHeader *& freeElem, size_t & size, size_t & alignment ) with ( heapManager ) {
     436static inline bool headers( const char name[] __attribute__(( unused )), void * addr, HeapManager.Storage.Header *& header, HeapManager.FreeHeader *& freeElem,
     437                                                        size_t & size, size_t & alignment ) with( heapManager ) {
    421438        header = headerAddr( addr );
    422439
    423         if ( unlikely( heapEnd < addr ) ) {                                     // mmapped ?
     440  if ( unlikely( addr < heapBegin || heapEnd < addr ) ) { // mmapped ?
    424441                fakeHeader( header, alignment );
    425442                size = header->kind.real.blockSize & -3;                // mmap size
     
    428445
    429446        #ifdef __CFA_DEBUG__
    430         checkHeader( addr < heapBegin || header < (HeapManager.Storage.Header *)heapBegin, name, addr ); // bad low address ?
     447        checkHeader( header < (HeapManager.Storage.Header *)heapBegin, name, addr ); // bad low address ?
    431448        #endif // __CFA_DEBUG__
    432449
     
    449466} // headers
    450467
    451 
    452 static inline void * extend( size_t size ) with ( heapManager ) {
     468#ifdef __CFA_DEBUG__
     469#if __SIZEOF_POINTER__ == 4
     470#define MASK 0xdeadbeef
     471#else
     472#define MASK 0xdeadbeefdeadbeef
     473#endif
     474#define STRIDE size_t
     475
     476static void * Memset( void * addr, STRIDE size ) {              // debug only
     477        if ( size % sizeof(STRIDE) != 0 ) abort( "Memset() : internal error, size %zd not multiple of %zd.", size, sizeof(STRIDE) );
     478        if ( (STRIDE)addr % sizeof(STRIDE) != 0 ) abort( "Memset() : internal error, addr %p not multiple of %zd.", addr, sizeof(STRIDE) );
     479
     480        STRIDE * end = (STRIDE *)addr + size / sizeof(STRIDE);
     481        for ( STRIDE * p = (STRIDE *)addr; p < end; p += 1 ) *p = MASK;
     482        return addr;
     483} // Memset
     484#endif // __CFA_DEBUG__
     485
     486
     487#define NO_MEMORY_MSG "insufficient heap memory available for allocating %zd new bytes."
     488
     489static inline void * extend( size_t size ) with( heapManager ) {
    453490        lock( extlock __cfaabi_dbg_ctx2 );
    454491        ptrdiff_t rem = heapRemaining - size;
     
    456493                // If the size requested is bigger than the current remaining storage, increase the size of the heap.
    457494
    458                 size_t increase = libCeiling( size > heapExpand ? size : heapExpand, libAlign() );
    459                 if ( sbrk( increase ) == (void *)-1 ) {
     495                size_t increase = ceiling2( size > heapExpand ? size : heapExpand, __page_size );
     496                // Do not call abort or strerror( errno ) as they may call malloc.
     497                if ( sbrk( increase ) == (void *)-1 ) {                 // failed, no memory ?
    460498                        unlock( extlock );
    461                         errno = ENOMEM;
    462                         return 0p;
     499                        __cfaabi_bits_print_nolock( STDERR_FILENO, NO_MEMORY_MSG, size );
     500                        _exit( EXIT_FAILURE );
     501                } // if
     502                if ( mprotect( (char *)heapEnd + heapRemaining, increase, __map_prot ) ) {
     503                        unlock( extlock );
     504                        __cfaabi_bits_print_nolock( STDERR_FILENO, "extend() : internal error, mprotect failure, heapEnd:%p size:%zd, errno:%d.\n", heapEnd, increase, errno );
     505                        _exit( EXIT_FAILURE );
    463506                } // if
    464507                #ifdef __STATISTICS__
     
    468511                #ifdef __CFA_DEBUG__
    469512                // Set new memory to garbage so subsequent uninitialized usages might fail.
    470                 memset( (char *)heapEnd + heapRemaining, '\377', increase );
     513                memset( (char *)heapEnd + heapRemaining, '\xde', increase );
     514                //Memset( (char *)heapEnd + heapRemaining, increase );
    471515                #endif // __CFA_DEBUG__
    472516                rem = heapRemaining + increase - size;
     
    481525
    482526
    483 static inline void * doMalloc( size_t size ) with ( heapManager ) {
     527static inline void * doMalloc( size_t size ) with( heapManager ) {
    484528        HeapManager.Storage * block;                                            // pointer to new block of storage
    485529
     
    487531        // along with the block and is a multiple of the alignment size.
    488532
    489   if ( unlikely( size > ~0ul - sizeof(HeapManager.Storage) ) ) return 0p;
     533  if ( unlikely( size > ULONG_MAX - sizeof(HeapManager.Storage) ) ) return 0p;
    490534        size_t tsize = size + sizeof(HeapManager.Storage);
    491535        if ( likely( tsize < mmapStart ) ) {                            // small size => sbrk
     
    497541                        posn = Bsearchl( (unsigned int)tsize, bucketSizes, (size_t)maxBucketsUsed );
    498542                HeapManager.FreeHeader * freeElem = &freeLists[posn];
    499                 // #ifdef FASTLOOKUP
    500                 // if ( tsize < LookupSizes )
    501                 //      freeElem = &freeLists[lookup[tsize]];
    502                 // else
    503                 // #endif // FASTLOOKUP
    504                 //      freeElem = bsearchl( tsize, freeLists, (size_t)maxBucketsUsed ); // binary search
    505                 // HeapManager.FreeHeader * freeElem =
    506                 //      #ifdef FASTLOOKUP
    507                 //      tsize < LookupSizes ? &freeLists[lookup[tsize]] :
    508                 //      #endif // FASTLOOKUP
    509                 //      bsearchl( tsize, freeLists, (size_t)maxBucketsUsed ); // binary search
    510                 assert( freeElem <= &freeLists[maxBucketsUsed] ); // subscripting error ?
    511                 assert( tsize <= freeElem->blockSize );                 // search failure ?
     543                verify( freeElem <= &freeLists[maxBucketsUsed] ); // subscripting error ?
     544                verify( tsize <= freeElem->blockSize );                 // search failure ?
    512545                tsize = freeElem->blockSize;                                    // total space needed for request
    513546
    514547                // Spin until the lock is acquired for this particular size of block.
    515548
    516                 #if defined( SPINLOCK )
     549                #if BUCKETLOCK == SPINLOCK
    517550                lock( freeElem->lock __cfaabi_dbg_ctx2 );
    518551                block = freeElem->freeList;                                             // remove node from stack
    519552                #else
    520                 block = freeElem->freeList.pop();
    521                 #endif // SPINLOCK
     553                block = pop( freeElem->freeList );
     554                #endif // BUCKETLOCK
    522555                if ( unlikely( block == 0p ) ) {                                // no free block ?
    523                         #if defined( SPINLOCK )
     556                        #if BUCKETLOCK == SPINLOCK
    524557                        unlock( freeElem->lock );
    525                         #endif // SPINLOCK
     558                        #endif // BUCKETLOCK
    526559
    527560                        // Freelist for that size was empty, so carve it out of the heap if there's enough left, or get some more
     
    529562
    530563                        block = (HeapManager.Storage *)extend( tsize ); // mutual exclusion on call
    531   if ( unlikely( block == 0p ) ) return 0p;
    532                 #if defined( SPINLOCK )
     564                #if BUCKETLOCK == SPINLOCK
    533565                } else {
    534566                        freeElem->freeList = block->header.kind.real.next;
    535567                        unlock( freeElem->lock );
    536                 #endif // SPINLOCK
     568                #endif // BUCKETLOCK
    537569                } // if
    538570
    539571                block->header.kind.real.home = freeElem;                // pointer back to free list of apropriate size
    540572        } else {                                                                                        // large size => mmap
    541   if ( unlikely( size > ~0ul - pageSize ) ) return 0p;
    542                 tsize = libCeiling( tsize, pageSize );                  // must be multiple of page size
     573  if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p;
     574                tsize = ceiling2( tsize, __page_size );                 // must be multiple of page size
    543575                #ifdef __STATISTICS__
    544576                __atomic_add_fetch( &mmap_calls, 1, __ATOMIC_SEQ_CST );
    545577                __atomic_add_fetch( &mmap_storage, tsize, __ATOMIC_SEQ_CST );
    546578                #endif // __STATISTICS__
    547                 block = (HeapManager.Storage *)mmap( 0, tsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 );
    548                 if ( block == (HeapManager.Storage *)MAP_FAILED ) {
     579
     580                block = (HeapManager.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 );
     581                if ( block == (HeapManager.Storage *)MAP_FAILED ) { // failed ?
     582                        if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory
    549583                        // Do not call strerror( errno ) as it may call malloc.
    550                         abort( "(HeapManager &)0x%p.doMalloc() : internal error, mmap failure, size:%zu error:%d.", &heapManager, tsize, errno );
    551                 } // if
     584                        abort( "(HeapManager &)0x%p.doMalloc() : internal error, mmap failure, size:%zu errno:%d.", &heapManager, tsize, errno );
     585                } //if
    552586                #ifdef __CFA_DEBUG__
    553587                // Set new memory to garbage so subsequent uninitialized usages might fail.
    554                 memset( block, '\377', tsize );
     588                memset( block, '\xde', tsize );
     589                //Memset( block, tsize );
    555590                #endif // __CFA_DEBUG__
    556591                block->header.kind.real.blockSize = tsize;              // storage size for munmap
    557592        } // if
    558593
     594        block->header.kind.real.size = size;                            // store allocation size
    559595        void * addr = &(block->data);                                           // adjust off header to user bytes
     596        verify( ((uintptr_t)addr & (libAlign() - 1)) == 0 ); // minimum alignment ?
    560597
    561598        #ifdef __CFA_DEBUG__
    562         assert( ((uintptr_t)addr & (libAlign() - 1)) == 0 ); // minimum alignment ?
    563         __atomic_add_fetch( &allocFree, tsize, __ATOMIC_SEQ_CST );
     599        __atomic_add_fetch( &allocUnfreed, tsize, __ATOMIC_SEQ_CST );
    564600        if ( traceHeap() ) {
    565601                enum { BufferSize = 64 };
    566602                char helpText[BufferSize];
    567603                int len = snprintf( helpText, BufferSize, "%p = Malloc( %zu ) (allocated %zu)\n", addr, size, tsize );
    568                 // int len = snprintf( helpText, BufferSize, "Malloc %p %zu\n", addr, size );
    569604                __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug
    570605        } // if
     
    575610
    576611
    577 static inline void doFree( void * addr ) with ( heapManager ) {
     612static inline void doFree( void * addr ) with( heapManager ) {
    578613        #ifdef __CFA_DEBUG__
    579614        if ( unlikely( heapManager.heapBegin == 0p ) ) {
     
    592627                #endif // __STATISTICS__
    593628                if ( munmap( header, size ) == -1 ) {
    594                         #ifdef __CFA_DEBUG__
    595629                        abort( "Attempt to deallocate storage %p not allocated or with corrupt header.\n"
    596630                                   "Possible cause is invalid pointer.",
    597631                                   addr );
    598                         #endif // __CFA_DEBUG__
    599632                } // if
    600633        } else {
    601634                #ifdef __CFA_DEBUG__
    602635                // Set free memory to garbage so subsequent usages might fail.
    603                 memset( ((HeapManager.Storage *)header)->data, '\377', freeElem->blockSize - sizeof( HeapManager.Storage ) );
     636                memset( ((HeapManager.Storage *)header)->data, '\xde', freeElem->blockSize - sizeof( HeapManager.Storage ) );
     637                //Memset( ((HeapManager.Storage *)header)->data, freeElem->blockSize - sizeof( HeapManager.Storage ) );
    604638                #endif // __CFA_DEBUG__
    605639
     
    607641                free_storage += size;
    608642                #endif // __STATISTICS__
    609                 #if defined( SPINLOCK )
     643                #if BUCKETLOCK == SPINLOCK
    610644                lock( freeElem->lock __cfaabi_dbg_ctx2 );               // acquire spin lock
    611645                header->kind.real.next = freeElem->freeList;    // push on stack
     
    613647                unlock( freeElem->lock );                                               // release spin lock
    614648                #else
    615                 freeElem->freeList.push( *(HeapManager.Storage *)header );
    616                 #endif // SPINLOCK
     649                push( freeElem->freeList, *(HeapManager.Storage *)header );
     650                #endif // BUCKETLOCK
    617651        } // if
    618652
    619653        #ifdef __CFA_DEBUG__
    620         __atomic_add_fetch( &allocFree, -size, __ATOMIC_SEQ_CST );
     654        __atomic_add_fetch( &allocUnfreed, -size, __ATOMIC_SEQ_CST );
    621655        if ( traceHeap() ) {
    622                 enum { BufferSize = 64 };
    623                 char helpText[BufferSize];
     656                char helpText[64];
    624657                int len = snprintf( helpText, sizeof(helpText), "Free( %p ) size:%zu\n", addr, size );
    625658                __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug
     
    629662
    630663
    631 size_t prtFree( HeapManager & manager ) with ( manager ) {
     664size_t prtFree( HeapManager & manager ) with( manager ) {
    632665        size_t total = 0;
    633666        #ifdef __STATISTICS__
     
    641674                #endif // __STATISTICS__
    642675
    643                 #if defined( SPINLOCK )
     676                #if BUCKETLOCK == SPINLOCK
    644677                for ( HeapManager.Storage * p = freeLists[i].freeList; p != 0p; p = p->header.kind.real.next ) {
    645678                #else
    646                 for ( HeapManager.Storage * p = freeLists[i].freeList.top(); p != 0p; p = p->header.kind.real.next.top ) {
    647                 #endif // SPINLOCK
     679                        for(;;) {
     680//              for ( HeapManager.Storage * p = top( freeLists[i].freeList ); p != 0p; p = (p)`next->top ) {
     681//              for ( HeapManager.Storage * p = top( freeLists[i].freeList ); p != 0p; /* p = getNext( p )->top */) {
     682//                      HeapManager.Storage * temp = p->header.kind.real.next.top; // FIX ME: direct assignent fails, initialization works`
     683//                      typeof(p) temp = (( p )`next)->top;                     // FIX ME: direct assignent fails, initialization works`
     684//                      p = temp;
     685                #endif // BUCKETLOCK
    648686                        total += size;
    649687                        #ifdef __STATISTICS__
     
    665703
    666704
    667 static void ?{}( HeapManager & manager ) with ( manager ) {
    668         pageSize = sysconf( _SC_PAGESIZE );
     705static void ?{}( HeapManager & manager ) with( manager ) {
     706        __page_size = sysconf( _SC_PAGESIZE );
     707        __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC;
    669708
    670709        for ( unsigned int i = 0; i < NoBucketSizes; i += 1 ) { // initialize the free lists
     
    680719        #endif // FASTLOOKUP
    681720
    682         if ( setMmapStart( default_mmap_start() ) ) {
     721        if ( ! setMmapStart( default_mmap_start() ) ) {
    683722                abort( "HeapManager : internal error, mmap start initialization failure." );
    684723        } // if
     
    686725
    687726        char * end = (char *)sbrk( 0 );
    688         sbrk( (char *)libCeiling( (long unsigned int)end, libAlign() ) - end ); // move start of heap to multiple of alignment
    689         heapBegin = heapEnd = sbrk( 0 );                                        // get new start point
     727        heapBegin = heapEnd = sbrk( (char *)ceiling2( (long unsigned int)end, __page_size ) - end ); // move start of heap to multiple of alignment
    690728} // HeapManager
    691729
     
    695733        if ( traceHeapTerm() ) {
    696734                printStats();
    697                 // if ( prtfree() ) prtFree( heapManager, true );
     735                // prtUnfreed() called in heapAppStop()
    698736        } // if
    699737        #endif // __STATISTICS__
     
    704742void memory_startup( void ) {
    705743        #ifdef __CFA_DEBUG__
    706         if ( unlikely( heapBoot ) ) {                                           // check for recursion during system boot
    707                 // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.
     744        if ( heapBoot ) {                                                                       // check for recursion during system boot
    708745                abort( "boot() : internal error, recursively invoked during system boot." );
    709746        } // if
     
    711748        #endif // __CFA_DEBUG__
    712749
    713         //assert( heapManager.heapBegin != 0 );
     750        //verify( heapManager.heapBegin != 0 );
    714751        //heapManager{};
    715         if ( heapManager.heapBegin == 0p ) heapManager{};
     752        if ( heapManager.heapBegin == 0p ) heapManager{};       // sanity check
    716753} // memory_startup
    717754
     
    723760
    724761static inline void * mallocNoStats( size_t size ) {             // necessary for malloc statistics
    725         //assert( heapManager.heapBegin != 0 );
    726         if ( unlikely( heapManager.heapBegin == 0p ) ) heapManager{}; // called before memory_startup ?
    727         void * addr = doMalloc( size );
    728         if ( unlikely( addr == 0p ) ) errno = ENOMEM;           // POSIX
    729         return addr;
     762        verify( heapManager.heapBegin != 0p );                          // called before memory_startup ?
     763  if ( unlikely( size ) == 0 ) return 0p;                               // 0 BYTE ALLOCATION RETURNS NULL POINTER
     764
     765#if __SIZEOF_POINTER__ == 8
     766        verify( size < ((typeof(size_t))1 << 48) );
     767#endif // __SIZEOF_POINTER__ == 8
     768        return doMalloc( size );
    730769} // mallocNoStats
    731770
    732771
    733 static inline void * callocNoStats( size_t noOfElems, size_t elemSize ) {
    734         size_t size = noOfElems * elemSize;
     772static inline void * callocNoStats( size_t dim, size_t elemSize ) {
     773        size_t size = dim * elemSize;
     774  if ( unlikely( size ) == 0 ) return 0p;                               // 0 BYTE ALLOCATION RETURNS NULL POINTER
    735775        char * addr = (char *)mallocNoStats( size );
    736   if ( unlikely( addr == 0p ) ) return 0p;
    737776
    738777        HeapManager.Storage.Header * header;
    739778        HeapManager.FreeHeader * freeElem;
    740779        size_t bsize, alignment;
    741         bool mapped __attribute__(( unused )) = headers( "calloc", addr, header, freeElem, bsize, alignment );
    742780        #ifndef __CFA_DEBUG__
     781        bool mapped =
     782        #endif // __CFA_DEBUG__
     783                headers( "calloc", addr, header, freeElem, bsize, alignment );
     784        #ifndef __CFA_DEBUG__
     785
    743786        // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
    744787        if ( ! mapped )
    745788        #endif // __CFA_DEBUG__
    746                 // Zero entire data space even when > than size => realloc without a new allocation and zero fill works.
    747                 // <-------00000000000000000000000000000000000000000000000000000> bsize (bucket size)
     789                // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined
    748790                // `-header`-addr                      `-size
    749                 memset( addr, '\0', bsize - sizeof(HeapManager.Storage) ); // set to zeros
     791                memset( addr, '\0', size );                                             // set to zeros
    750792
    751793        header->kind.real.blockSize |= 2;                                       // mark as zero filled
     
    754796
    755797
    756 static inline void * memalignNoStats( size_t alignment, size_t size ) { // necessary for malloc statistics
     798static inline void * memalignNoStats( size_t alignment, size_t size ) {
     799  if ( unlikely( size ) == 0 ) return 0p;                               // 0 BYTE ALLOCATION RETURNS NULL POINTER
     800
    757801        #ifdef __CFA_DEBUG__
    758802        checkAlign( alignment );                                                        // check alignment
     
    772816        // add sizeof(Storage) for fake header
    773817        char * addr = (char *)mallocNoStats( size + alignment - libAlign() + sizeof(HeapManager.Storage) );
    774   if ( unlikely( addr == 0p ) ) return addr;
    775818
    776819        // address in the block of the "next" alignment address
    777         char * user = (char *)libCeiling( (uintptr_t)(addr + sizeof(HeapManager.Storage)), alignment );
     820        char * user = (char *)ceiling2( (uintptr_t)(addr + sizeof(HeapManager.Storage)), alignment );
    778821
    779822        // address of header from malloc
    780823        HeapManager.Storage.Header * realHeader = headerAddr( addr );
     824        realHeader->kind.real.size = size;                                      // correct size to eliminate above alignment offset
    781825        // address of fake header * before* the alignment location
    782826        HeapManager.Storage.Header * fakeHeader = headerAddr( user );
     
    790834
    791835
    792 static inline void * cmemalignNoStats( size_t alignment, size_t noOfElems, size_t elemSize ) {
    793         size_t size = noOfElems * elemSize;
     836static inline void * cmemalignNoStats( size_t alignment, size_t dim, size_t elemSize ) {
     837        size_t size = dim * elemSize;
     838  if ( unlikely( size ) == 0 ) return 0p;                               // 0 BYTE ALLOCATION RETURNS NULL POINTER
    794839        char * addr = (char *)memalignNoStats( alignment, size );
    795   if ( unlikely( addr == 0p ) ) return 0p;
     840
    796841        HeapManager.Storage.Header * header;
    797842        HeapManager.FreeHeader * freeElem;
    798843        size_t bsize;
    799         bool mapped __attribute__(( unused )) = headers( "cmemalign", addr, header, freeElem, bsize, alignment );
    800844        #ifndef __CFA_DEBUG__
     845        bool mapped =
     846        #endif // __CFA_DEBUG__
     847                headers( "cmemalign", addr, header, freeElem, bsize, alignment );
     848
    801849        // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
     850        #ifndef __CFA_DEBUG__
    802851        if ( ! mapped )
    803852        #endif // __CFA_DEBUG__
    804                 memset( addr, '\0', dataStorage( bsize, addr, header ) ); // set to zeros
    805         header->kind.real.blockSize |= 2;                               // mark as zero filled
    806 
     853                // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined
     854                // `-header`-addr                      `-size
     855                memset( addr, '\0', size );                                             // set to zeros
     856
     857        header->kind.real.blockSize |= 2;                                       // mark as zero filled
    807858        return addr;
    808859} // cmemalignNoStats
    809860
    810861
    811 // supported mallopt options
    812 #ifndef M_MMAP_THRESHOLD
    813 #define M_MMAP_THRESHOLD (-1)
    814 #endif // M_TOP_PAD
    815 #ifndef M_TOP_PAD
    816 #define M_TOP_PAD (-2)
    817 #endif // M_TOP_PAD
    818 
    819 
    820862extern "C" {
    821         // The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not
    822         // initialized. If size is 0, then malloc() returns either 0p, or a unique pointer value that can later be
    823         // successfully passed to free().
     863        // Allocates size bytes and returns a pointer to the allocated memory.  The contents are undefined. If size is 0,
     864        // then malloc() returns a unique pointer value that can later be successfully passed to free().
    824865        void * malloc( size_t size ) {
    825866                #ifdef __STATISTICS__
     
    831872        } // malloc
    832873
    833         // The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to
    834         // the allocated memory. The memory is set to zero. If nmemb or size is 0, then calloc() returns either 0p, or a
    835         // unique pointer value that can later be successfully passed to free().
    836         void * calloc( size_t noOfElems, size_t elemSize ) {
     874
     875        // Same as malloc() except size bytes is an array of dim elements each of elemSize bytes.
     876        void * aalloc( size_t dim, size_t elemSize ) {
     877                size_t size = dim * elemSize;
     878                #ifdef __STATISTICS__
     879                __atomic_add_fetch( &aalloc_calls, 1, __ATOMIC_SEQ_CST );
     880                __atomic_add_fetch( &aalloc_storage, size, __ATOMIC_SEQ_CST );
     881                #endif // __STATISTICS__
     882
     883                return mallocNoStats( size );
     884        } // aalloc
     885
     886
     887        // Same as aalloc() with memory set to zero.
     888        void * calloc( size_t dim, size_t elemSize ) {
    837889                #ifdef __STATISTICS__
    838890                __atomic_add_fetch( &calloc_calls, 1, __ATOMIC_SEQ_CST );
    839                 __atomic_add_fetch( &calloc_storage, noOfElems * elemSize, __ATOMIC_SEQ_CST );
    840                 #endif // __STATISTICS__
    841 
    842                 return callocNoStats( noOfElems, elemSize );
     891                __atomic_add_fetch( &calloc_storage, dim * elemSize, __ATOMIC_SEQ_CST );
     892                #endif // __STATISTICS__
     893
     894                return callocNoStats( dim, elemSize );
    843895        } // calloc
    844896
    845         // The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be
    846         // unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size
    847         // is larger than the old size, the added memory will not be initialized.  If ptr is 0p, then the call is
    848         // equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not 0p, then the call
    849         // is equivalent to free(ptr). Unless ptr is 0p, it must have been returned by an earlier call to malloc(),
    850         // calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done.
    851         void * realloc( void * oaddr, size_t size ) {
    852                 #ifdef __STATISTICS__
    853                 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST );
     897
     898        // Change the size of the memory block pointed to by oaddr to size bytes. The contents are undefined.  If oaddr is
     899        // 0p, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and oaddr is
     900        // not 0p, then the call is equivalent to free(oaddr). Unless oaddr is 0p, it must have been returned by an earlier
     901        // call to malloc(), alloc(), calloc() or realloc(). If the area pointed to was moved, a free(oaddr) is done.
     902        void * resize( void * oaddr, size_t size ) {
     903                #ifdef __STATISTICS__
     904                __atomic_add_fetch( &resize_calls, 1, __ATOMIC_SEQ_CST );
    854905                #endif // __STATISTICS__
    855906
    856907                // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
    857           if ( unlikely( size == 0 ) ) { free( oaddr ); return mallocNoStats( size ); } // special cases
    858           if ( unlikely( oaddr == 0p ) ) return mallocNoStats( size );
     908          if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases
     909          if ( unlikely( oaddr == 0p ) ) {
     910                        #ifdef __STATISTICS__
     911                        __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST );
     912                        #endif // __STATISTICS__
     913                        return mallocNoStats( size );
     914                } // if
    859915
    860916                HeapManager.Storage.Header * header;
    861917                HeapManager.FreeHeader * freeElem;
    862                 size_t bsize, oalign = 0;
     918                size_t bsize, oalign;
     919                headers( "resize", oaddr, header, freeElem, bsize, oalign );
     920                size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket
     921
     922                // same size, DO NOT preserve STICKY PROPERTIES.
     923                if ( oalign == libAlign() && size <= odsize && odsize <= size * 2 ) { // allow 50% wasted storage for smaller size
     924                        header->kind.real.blockSize &= -2;                      // no alignment and turn off 0 fill
     925                        header->kind.real.size = size;                          // reset allocation size
     926                        return oaddr;
     927                } // if
     928
     929                #ifdef __STATISTICS__
     930                __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST );
     931                #endif // __STATISTICS__
     932
     933                // change size, DO NOT preserve STICKY PROPERTIES.
     934                free( oaddr );
     935                return mallocNoStats( size );                                   // create new area
     936        } // resize
     937
     938
     939        // Same as resize() but the contents are unchanged in the range from the start of the region up to the minimum of
     940        // the old and new sizes.
     941        void * realloc( void * oaddr, size_t size ) {
     942                #ifdef __STATISTICS__
     943                __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST );
     944                #endif // __STATISTICS__
     945
     946                // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
     947          if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases
     948          if ( unlikely( oaddr == 0p ) ) {
     949                        #ifdef __STATISTICS__
     950                        __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST );
     951                        #endif // __STATISTICS__
     952                        return mallocNoStats( size );
     953                } // if
     954
     955                HeapManager.Storage.Header * header;
     956                HeapManager.FreeHeader * freeElem;
     957                size_t bsize, oalign;
    863958                headers( "realloc", oaddr, header, freeElem, bsize, oalign );
    864959
    865960                size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket
    866           if ( size <= odsize && odsize <= size * 2 ) { // allow up to 50% wasted storage in smaller size
    867                         // Do not know size of original allocation => cannot do 0 fill for any additional space because do not know
    868                         // where to start filling, i.e., do not overwrite existing values in space.
    869                         //
    870                         // This case does not result in a new profiler entry because the previous one still exists and it must match with
    871                         // the free for this memory.  Hence, this realloc does not appear in the profiler output.
     961                size_t osize = header->kind.real.size;                  // old allocation size
     962                bool ozfill = (header->kind.real.blockSize & 2); // old allocation zero filled
     963          if ( unlikely( size <= odsize ) && odsize <= size * 2 ) { // allow up to 50% wasted storage
     964                        header->kind.real.size = size;                          // reset allocation size
     965                        if ( unlikely( ozfill ) && size > osize ) {     // previous request zero fill and larger ?
     966                                memset( (char *)oaddr + osize, '\0', size - osize ); // initialize added storage
     967                        } // if
    872968                        return oaddr;
    873969                } // if
    874970
    875971                #ifdef __STATISTICS__
    876                 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST );
     972                __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST );
    877973                #endif // __STATISTICS__
    878974
     
    880976
    881977                void * naddr;
    882                 if ( unlikely( oalign != 0 ) ) {                                // previous request memalign?
    883                         if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill
    884                                 naddr = cmemalignNoStats( oalign, 1, size ); // create new aligned area
    885                         } else {
    886                                 naddr = memalignNoStats( oalign, size ); // create new aligned area
     978                if ( likely( oalign == libAlign() ) ) {                 // previous request not aligned ?
     979                        naddr = mallocNoStats( size );                          // create new area
     980                } else {
     981                        naddr = memalignNoStats( oalign, size );        // create new aligned area
     982                } // if
     983
     984                headers( "realloc", naddr, header, freeElem, bsize, oalign );
     985                memcpy( naddr, oaddr, min( osize, size ) );             // copy bytes
     986                free( oaddr );
     987
     988                if ( unlikely( ozfill ) ) {                                             // previous request zero fill ?
     989                        header->kind.real.blockSize |= 2;                       // mark new request as zero filled
     990                        if ( size > osize ) {                                           // previous request larger ?
     991                                memset( (char *)naddr + osize, '\0', size - osize ); // initialize added storage
    887992                        } // if
    888                 } else {
    889                         if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill
    890                                 naddr = callocNoStats( 1, size );               // create new area
    891                         } else {
    892                                 naddr = mallocNoStats( size );                  // create new area
    893                         } // if
    894                 } // if
    895           if ( unlikely( naddr == 0p ) ) return 0p;
    896 
    897                 headers( "realloc", naddr, header, freeElem, bsize, oalign );
    898                 size_t ndsize = dataStorage( bsize, naddr, header ); // data storage avilable in bucket
    899                 // To preserve prior fill, the entire bucket must be copied versus the size.
    900                 memcpy( naddr, oaddr, MIN( odsize, ndsize ) );  // copy bytes
    901                 free( oaddr );
     993                } // if
    902994                return naddr;
    903995        } // realloc
    904996
    905         // The obsolete function memalign() allocates size bytes and returns a pointer to the allocated memory. The memory
    906         // address will be a multiple of alignment, which must be a power of two.
     997
     998        // Same as malloc() except the memory address is a multiple of alignment, which must be a power of two. (obsolete)
    907999        void * memalign( size_t alignment, size_t size ) {
    9081000                #ifdef __STATISTICS__
     
    9151007
    9161008
    917         // The cmemalign() function is the same as calloc() with memory alignment.
    918         void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize ) {
     1009        // Same as aalloc() with memory alignment.
     1010        void * amemalign( size_t alignment, size_t dim, size_t elemSize ) {
     1011                size_t size = dim * elemSize;
    9191012                #ifdef __STATISTICS__
    9201013                __atomic_add_fetch( &cmemalign_calls, 1, __ATOMIC_SEQ_CST );
    921                 __atomic_add_fetch( &cmemalign_storage, noOfElems * elemSize, __ATOMIC_SEQ_CST );
    922                 #endif // __STATISTICS__
    923 
    924                 return cmemalignNoStats( alignment, noOfElems, elemSize );
     1014                __atomic_add_fetch( &cmemalign_storage, size, __ATOMIC_SEQ_CST );
     1015                #endif // __STATISTICS__
     1016
     1017                return memalignNoStats( alignment, size );
     1018        } // amemalign
     1019
     1020
     1021        // Same as calloc() with memory alignment.
     1022        void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) {
     1023                #ifdef __STATISTICS__
     1024                __atomic_add_fetch( &cmemalign_calls, 1, __ATOMIC_SEQ_CST );
     1025                __atomic_add_fetch( &cmemalign_storage, dim * elemSize, __ATOMIC_SEQ_CST );
     1026                #endif // __STATISTICS__
     1027
     1028                return cmemalignNoStats( alignment, dim, elemSize );
    9251029        } // cmemalign
    9261030
    927         // The function aligned_alloc() is the same as memalign(), except for the added restriction that size should be a
    928         // multiple of alignment.
     1031
     1032        // Same as memalign(), but ISO/IEC 2011 C11 Section 7.22.2 states: the value of size shall be an integral multiple
     1033    // of alignment. This requirement is universally ignored.
    9291034        void * aligned_alloc( size_t alignment, size_t size ) {
    9301035                return memalign( alignment, size );
     
    9321037
    9331038
    934         // The function posix_memalign() allocates size bytes and places the address of the allocated memory in *memptr. The
    935         // address of the allocated memory will be a multiple of alignment, which must be a power of two and a multiple of
    936         // sizeof(void *). If size is 0, then posix_memalign() returns either 0p, or a unique pointer value that can later
    937         // be successfully passed to free(3).
     1039        // Allocates size bytes and places the address of the allocated memory in *memptr. The address of the allocated
     1040        // memory shall be a multiple of alignment, which must be a power of two and a multiple of sizeof(void *). If size
     1041        // is 0, then posix_memalign() returns either 0p, or a unique pointer value that can later be successfully passed to
     1042        // free(3).
    9381043        int posix_memalign( void ** memptr, size_t alignment, size_t size ) {
    939           if ( alignment < sizeof(void *) || ! libPow2( alignment ) ) return EINVAL; // check alignment
     1044          if ( alignment < libAlign() || ! is_pow2( alignment ) ) return EINVAL; // check alignment
    9401045                * memptr = memalign( alignment, size );
    941           if ( unlikely( * memptr == 0p ) ) return ENOMEM;
    9421046                return 0;
    9431047        } // posix_memalign
    9441048
    945         // The obsolete function valloc() allocates size bytes and returns a pointer to the allocated memory. The memory
    946         // address will be a multiple of the page size.  It is equivalent to memalign(sysconf(_SC_PAGESIZE),size).
     1049
     1050        // Allocates size bytes and returns a pointer to the allocated memory. The memory address shall be a multiple of the
     1051        // page size.  It is equivalent to memalign(sysconf(_SC_PAGESIZE),size).
    9471052        void * valloc( size_t size ) {
    948                 return memalign( pageSize, size );
     1053                return memalign( __page_size, size );
    9491054        } // valloc
    9501055
    9511056
    952         // The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to
    953         // malloc(), calloc() or realloc().  Otherwise, or if free(ptr) has already been called before, undefined behavior
    954         // occurs. If ptr is 0p, no operation is performed.
     1057        // Same as valloc but rounds size to multiple of page size.
     1058        void * pvalloc( size_t size ) {
     1059                return memalign( __page_size, ceiling2( size, __page_size ) );
     1060        } // pvalloc
     1061
     1062
     1063        // Frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc()
     1064        // or realloc().  Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs. If ptr is
     1065        // 0p, no operation is performed.
    9551066        void free( void * addr ) {
    9561067                #ifdef __STATISTICS__
     
    9731084
    9741085
    975         // The malloc_alignment() function returns the alignment of the allocation.
     1086        // Returns the alignment of an allocation.
    9761087        size_t malloc_alignment( void * addr ) {
    9771088          if ( unlikely( addr == 0p ) ) return libAlign();      // minimum alignment
     
    9801091                        return header->kind.fake.alignment & -2;        // remove flag from value
    9811092                } else {
    982                         return libAlign ();                                                     // minimum alignment
     1093                        return libAlign();                                                      // minimum alignment
    9831094                } // if
    9841095        } // malloc_alignment
    9851096
    9861097
    987         // The malloc_zero_fill() function returns true if the allocation is zero filled, i.e., initially allocated by calloc().
     1098        // Set the alignment for an the allocation and return previous alignment or 0 if no alignment.
     1099        size_t $malloc_alignment_set( void * addr, size_t alignment ) {
     1100          if ( unlikely( addr == 0p ) ) return libAlign();      // minimum alignment
     1101                size_t ret;
     1102                HeapManager.Storage.Header * header = headerAddr( addr );
     1103                if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ?
     1104                        ret = header->kind.fake.alignment & -2;         // remove flag from old value
     1105                        header->kind.fake.alignment = alignment | 1; // add flag to new value
     1106                } else {
     1107                        ret = 0;                                                                        // => no alignment to change
     1108                } // if
     1109                return ret;
     1110        } // $malloc_alignment_set
     1111
     1112
     1113        // Returns true if the allocation is zero filled, e.g., allocated by calloc().
    9881114        bool malloc_zero_fill( void * addr ) {
    9891115          if ( unlikely( addr == 0p ) ) return false;           // null allocation is not zero fill
    9901116                HeapManager.Storage.Header * header = headerAddr( addr );
    9911117                if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ?
    992                         header = (HeapManager.Storage.Header *)((char *)header - header->kind.fake.offset);
    993                 } // if
    994                 return (header->kind.real.blockSize & 2) != 0;  // zero filled (calloc/cmemalign) ?
     1118                        header = realHeader( header );                          // backup from fake to real header
     1119                } // if
     1120                return (header->kind.real.blockSize & 2) != 0;  // zero filled ?
    9951121        } // malloc_zero_fill
    9961122
    997 
    998         // The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to
    999         // a block of memory allocated by malloc(3) or a related function.
     1123        // Set allocation is zero filled and return previous zero filled.
     1124        bool $malloc_zero_fill_set( void * addr ) {
     1125          if ( unlikely( addr == 0p ) ) return false;           // null allocation is not zero fill
     1126                HeapManager.Storage.Header * header = headerAddr( addr );
     1127                if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ?
     1128                        header = realHeader( header );                          // backup from fake to real header
     1129                } // if
     1130                bool ret = (header->kind.real.blockSize & 2) != 0; // zero filled ?
     1131                header->kind.real.blockSize |= 2;                               // mark as zero filled
     1132                return ret;
     1133        } // $malloc_zero_fill_set
     1134
     1135
     1136        // Returns original total allocation size (not bucket size) => array size is dimension * sizeif(T).
     1137        size_t malloc_size( void * addr ) {
     1138          if ( unlikely( addr == 0p ) ) return 0;                       // null allocation has zero size
     1139                HeapManager.Storage.Header * header = headerAddr( addr );
     1140                if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ?
     1141                        header = realHeader( header );                          // backup from fake to real header
     1142                } // if
     1143                return header->kind.real.size;
     1144        } // malloc_size
     1145
     1146        // Set allocation size and return previous size.
     1147        size_t $malloc_size_set( void * addr, size_t size ) {
     1148          if ( unlikely( addr == 0p ) ) return 0;                       // null allocation has 0 size
     1149                HeapManager.Storage.Header * header = headerAddr( addr );
     1150                if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ?
     1151                        header = realHeader( header );                          // backup from fake to real header
     1152                } // if
     1153                size_t ret = header->kind.real.size;
     1154                header->kind.real.size = size;
     1155                return ret;
     1156        } // $malloc_size_set
     1157
     1158
     1159        // Returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by
     1160        // malloc or a related function.
    10001161        size_t malloc_usable_size( void * addr ) {
    10011162          if ( unlikely( addr == 0p ) ) return 0;                       // null allocation has 0 size
     
    10051166
    10061167                headers( "malloc_usable_size", addr, header, freeElem, bsize, alignment );
    1007                 return dataStorage( bsize, addr, header );      // data storage in bucket
     1168                return dataStorage( bsize, addr, header );              // data storage in bucket
    10081169        } // malloc_usable_size
    10091170
    10101171
    1011         // The malloc_stats() function prints (on default standard error) statistics about memory allocated by malloc(3) and
    1012         // related functions.
     1172        // Prints (on default standard error) statistics about memory allocated by malloc and related functions.
    10131173        void malloc_stats( void ) {
    10141174                #ifdef __STATISTICS__
     
    10181178        } // malloc_stats
    10191179
    1020         // The malloc_stats_fd() function changes the file descripter where malloc_stats() writes the statistics.
     1180
     1181        // Changes the file descripter where malloc_stats() writes statistics.
    10211182        int malloc_stats_fd( int fd __attribute__(( unused )) ) {
    10221183                #ifdef __STATISTICS__
    1023                 int temp = statfd;
    1024                 statfd = fd;
     1184                int temp = stat_fd;
     1185                stat_fd = fd;
    10251186                return temp;
    10261187                #else
     
    10301191
    10311192
    1032         // The mallopt() function adjusts parameters that control the behavior of the memory-allocation functions (see
    1033         // malloc(3)). The param argument specifies the parameter to be modified, and value specifies the new value for that
    1034         // parameter.
     1193        // Adjusts parameters that control the behaviour of the memory-allocation functions (see malloc). The param argument
     1194        // specifies the parameter to be modified, and value specifies the new value for that parameter.
    10351195        int mallopt( int option, int value ) {
    10361196                choose( option ) {
    10371197                  case M_TOP_PAD:
    1038                         if ( setHeapExpand( value ) ) return 1;
     1198                        heapExpand = ceiling2( value, __page_size ); return 1;
    10391199                  case M_MMAP_THRESHOLD:
    10401200                        if ( setMmapStart( value ) ) return 1;
     1201                        break;
    10411202                } // switch
    10421203                return 0;                                                                               // error, unsupported
    10431204        } // mallopt
    10441205
    1045         // The malloc_trim() function attempts to release free memory at the top of the heap (by calling sbrk(2) with a
    1046         // suitable argument).
     1206
     1207        // Attempt to release free memory at the top of the heap (by calling sbrk with a suitable argument).
    10471208        int malloc_trim( size_t ) {
    10481209                return 0;                                                                               // => impossible to release memory
     
    10501211
    10511212
    1052         // The malloc_info() function exports an XML string that describes the current state of the memory-allocation
    1053         // implementation in the caller.  The string is printed on the file stream stream.  The exported string includes
    1054         // information about all arenas (see malloc(3)).
     1213        // Exports an XML string that describes the current state of the memory-allocation implementation in the caller.
     1214        // The string is printed on the file stream stream.  The exported string includes information about all arenas (see
     1215        // malloc).
    10551216        int malloc_info( int options, FILE * stream ) {
    1056                 if ( options != 0 ) { errno = EINVAL; return -1; }
     1217          if ( options != 0 ) { errno = EINVAL; return -1; }
     1218                #ifdef __STATISTICS__
    10571219                return printStatsXML( stream );
     1220                #else
     1221                return 0;                                                                               // unsupported
     1222                #endif // __STATISTICS__
    10581223        } // malloc_info
    10591224
    10601225
    1061         // The malloc_get_state() function records the current state of all malloc(3) internal bookkeeping variables (but
    1062         // not the actual contents of the heap or the state of malloc_hook(3) functions pointers).  The state is recorded in
    1063         // a system-dependent opaque data structure dynamically allocated via malloc(3), and a pointer to that data
    1064         // structure is returned as the function result.  (It is the caller's responsibility to free(3) this memory.)
     1226        // Records the current state of all malloc internal bookkeeping variables (but not the actual contents of the heap
     1227        // or the state of malloc_hook functions pointers).  The state is recorded in a system-dependent opaque data
     1228        // structure dynamically allocated via malloc, and a pointer to that data structure is returned as the function
     1229        // result.  (The caller must free this memory.)
    10651230        void * malloc_get_state( void ) {
    10661231                return 0p;                                                                              // unsupported
     
    10681233
    10691234
    1070         // The malloc_set_state() function restores the state of all malloc(3) internal bookkeeping variables to the values
    1071         // recorded in the opaque data structure pointed to by state.
    1072         int malloc_set_state( void * ptr ) {
     1235        // Restores the state of all malloc internal bookkeeping variables to the values recorded in the opaque data
     1236        // structure pointed to by state.
     1237        int malloc_set_state( void * ) {
    10731238                return 0;                                                                               // unsupported
    10741239        } // malloc_set_state
     
    10771242
    10781243// Must have CFA linkage to overload with C linkage realloc.
    1079 void * realloc( void * oaddr, size_t nalign, size_t size ) {
     1244void * resize( void * oaddr, size_t nalign, size_t size ) {
    10801245        #ifdef __STATISTICS__
    1081         __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST );
     1246        __atomic_add_fetch( &resize_calls, 1, __ATOMIC_SEQ_CST );
    10821247        #endif // __STATISTICS__
    10831248
    1084         // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
    1085   if ( unlikely( size == 0 ) ) { free( oaddr ); return mallocNoStats( size ); } // special cases
    1086   if ( unlikely( oaddr == 0p ) ) return mallocNoStats( size );
    1087 
    1088         if ( unlikely( nalign == 0 ) ) nalign = libAlign();     // reset alignment to minimum
     1249        if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum
    10891250        #ifdef __CFA_DEBUG__
    10901251        else
     
    10921253        #endif // __CFA_DEBUG__
    10931254
    1094         HeapManager.Storage.Header * header;
    1095         HeapManager.FreeHeader * freeElem;
    1096         size_t bsize, oalign = 0;
    1097         headers( "realloc", oaddr, header, freeElem, bsize, oalign );
    1098         size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket
    1099 
    1100   if ( oalign != 0 && (uintptr_t)oaddr % nalign == 0 ) { // has alignment and just happens to work out
    1101                 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same)
    1102                 return realloc( oaddr, size );
     1255        // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
     1256  if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases
     1257  if ( unlikely( oaddr == 0p ) ) {
     1258                #ifdef __STATISTICS__
     1259                __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST );
     1260                #endif // __STATISTICS__
     1261                return memalignNoStats( nalign, size );
     1262        } // if
     1263
     1264        // Attempt to reuse existing alignment.
     1265        HeapManager.Storage.Header * header = headerAddr( oaddr );
     1266        bool isFakeHeader = header->kind.fake.alignment & 1; // old fake header ?
     1267        size_t oalign;
     1268        if ( isFakeHeader ) {
     1269                oalign = header->kind.fake.alignment & -2;              // old alignment
     1270                if ( (uintptr_t)oaddr % nalign == 0                             // lucky match ?
     1271                         && ( oalign <= nalign                                          // going down
     1272                                  || (oalign >= nalign && oalign <= 256) ) // little alignment storage wasted ?
     1273                        ) {
     1274                        headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same)
     1275                        HeapManager.FreeHeader * freeElem;
     1276                        size_t bsize, oalign;
     1277                        headers( "resize", oaddr, header, freeElem, bsize, oalign );
     1278                        size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket
     1279
     1280                        if ( size <= odsize && odsize <= size * 2 ) { // allow 50% wasted data storage
     1281                                headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same)
     1282
     1283                                header->kind.real.blockSize &= -2;              // turn off 0 fill
     1284                                header->kind.real.size = size;                  // reset allocation size
     1285                                return oaddr;
     1286                        } // if
     1287                } // if
     1288        } else if ( ! isFakeHeader                                                      // old real header (aligned on libAlign) ?
     1289                                && nalign == libAlign() ) {                             // new alignment also on libAlign => no fake header needed
     1290                return resize( oaddr, size );                                   // duplicate special case checks
    11031291        } // if
    11041292
    11051293        #ifdef __STATISTICS__
     1294        __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST );
     1295        #endif // __STATISTICS__
     1296
     1297        // change size, DO NOT preserve STICKY PROPERTIES.
     1298        free( oaddr );
     1299        return memalignNoStats( nalign, size );                         // create new aligned area
     1300} // resize
     1301
     1302
     1303void * realloc( void * oaddr, size_t nalign, size_t size ) {
     1304        if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum
     1305        #ifdef __CFA_DEBUG__
     1306        else
     1307                checkAlign( nalign );                                                   // check alignment
     1308        #endif // __CFA_DEBUG__
     1309
     1310        // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
     1311  if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases
     1312  if ( unlikely( oaddr == 0p ) ) {
     1313                #ifdef __STATISTICS__
     1314                __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST );
     1315                __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST );
     1316                #endif // __STATISTICS__
     1317                return memalignNoStats( nalign, size );
     1318        } // if
     1319
     1320        // Attempt to reuse existing alignment.
     1321        HeapManager.Storage.Header * header = headerAddr( oaddr );
     1322        bool isFakeHeader = header->kind.fake.alignment & 1; // old fake header ?
     1323        size_t oalign;
     1324        if ( isFakeHeader ) {
     1325                oalign = header->kind.fake.alignment & -2;              // old alignment
     1326                if ( (uintptr_t)oaddr % nalign == 0                             // lucky match ?
     1327                         && ( oalign <= nalign                                          // going down
     1328                                  || (oalign >= nalign && oalign <= 256) ) // little alignment storage wasted ?
     1329                        ) {
     1330                        headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same)
     1331                        return realloc( oaddr, size );                          // duplicate alignment and special case checks
     1332                } // if
     1333        } else if ( ! isFakeHeader                                                      // old real header (aligned on libAlign) ?
     1334                                && nalign == libAlign() )                               // new alignment also on libAlign => no fake header needed
     1335                return realloc( oaddr, size );                                  // duplicate alignment and special case checks
     1336
     1337        #ifdef __STATISTICS__
     1338        __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST );
    11061339        __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST );
    11071340        #endif // __STATISTICS__
    11081341
     1342        HeapManager.FreeHeader * freeElem;
     1343        size_t bsize;
     1344        headers( "realloc", oaddr, header, freeElem, bsize, oalign );
     1345
    11091346        // change size and copy old content to new storage
    11101347
    1111         void * naddr;
    1112         if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill
    1113                 naddr = cmemalignNoStats( nalign, 1, size );    // create new aligned area
    1114         } else {
    1115                 naddr = memalignNoStats( nalign, size );                // create new aligned area
    1116         } // if
     1348        size_t osize = header->kind.real.size;                          // old allocation size
     1349        bool ozfill = (header->kind.real.blockSize & 2);        // old allocation zero filled
     1350
     1351        void * naddr = memalignNoStats( nalign, size );         // create new aligned area
    11171352
    11181353        headers( "realloc", naddr, header, freeElem, bsize, oalign );
    1119         size_t ndsize = dataStorage( bsize, naddr, header ); // data storage avilable in bucket
    1120         // To preserve prior fill, the entire bucket must be copied versus the size.
    1121         memcpy( naddr, oaddr, MIN( odsize, ndsize ) );          // copy bytes
     1354        memcpy( naddr, oaddr, min( osize, size ) );                     // copy bytes
    11221355        free( oaddr );
     1356
     1357        if ( unlikely( ozfill ) ) {                                                     // previous request zero fill ?
     1358                header->kind.real.blockSize |= 2;                               // mark new request as zero filled
     1359                if ( size > osize ) {                                                   // previous request larger ?
     1360                        memset( (char *)naddr + osize, '\0', size - osize ); // initialize added storage
     1361                } // if
     1362        } // if
    11231363        return naddr;
    11241364} // realloc
  • libcfa/src/interpose.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed Mar 29 16:10:31 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Feb 17 10:18:53 2020
    13 // Update Count     : 166
     12// Last Modified On : Fri Mar 13 17:35:37 2020
     13// Update Count     : 178
    1414//
    1515
    1616#include <stdarg.h>                                                                             // va_start, va_end
     17#include <stdio.h>
    1718#include <string.h>                                                                             // strlen
    1819#include <unistd.h>                                                                             // _exit, getpid
     
    143144void abort( const char fmt[], ... ) __attribute__(( format(printf, 1, 2), __nothrow__, __leaf__, __noreturn__ ));
    144145void abort( bool signalAbort, const char fmt[], ... ) __attribute__(( format(printf, 2, 3), __nothrow__, __leaf__, __noreturn__ ));
     146void __abort( bool signalAbort, const char fmt[], va_list args ) __attribute__(( __nothrow__, __leaf__, __noreturn__ ));
    145147
    146148extern "C" {
     
    152154                va_list argp;
    153155                va_start( argp, fmt );
    154                 abort( false, fmt, argp );
     156                __abort( false, fmt, argp );
    155157                va_end( argp );
    156158        }
     
    218220}
    219221
    220 void abort( bool signalAbort, const char fmt[], ... ) {
    221         void * kernel_data = kernel_abort();                            // must be done here to lock down kernel
    222         int len;
    223 
    224         signal( SIGABRT, SIG_DFL );                                                     // prevent final "real" abort from recursing to handler
    225 
    226         len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld) ", (long int)getpid() ); // use UNIX pid (versus getPid)
    227         __cfaabi_bits_write( STDERR_FILENO, abort_text, len );
    228 
    229         assert( fmt );
    230         va_list args;
    231         va_start( args, fmt );
    232 
    233         len = vsnprintf( abort_text, abort_text_size, fmt, args );
    234         va_end( args );
    235         __cfaabi_bits_write( STDERR_FILENO, abort_text, len );
    236 
    237         if ( fmt[strlen( fmt ) - 1] != '\n' ) {                         // add optional newline if missing at the end of the format text
    238                 __cfaabi_dbg_write( "\n", 1 );
    239         } // if
    240         kernel_abort_msg( kernel_data, abort_text, abort_text_size );
    241 
    242         __cfaabi_backtrace( signalAbort ? 4 : 2 );
    243 
    244         __cabi_libc.abort();                                                            // print stack trace in handler
     222static volatile int __abort_stage = 0;
     223
     224// Cannot forward va_list.
     225void __abort( bool signalAbort, const char fmt[], va_list args ) {
     226        int stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST );
     227
     228        // First stage: stop the cforall kernel and print
     229        if(stage == 1) {
     230                // increment stage
     231                stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST );
     232
     233                // must be done here to lock down kernel
     234                void * kernel_data = kernel_abort();
     235                int len;
     236
     237                signal( SIGABRT, SIG_DFL );                                                     // prevent final "real" abort from recursing to handler
     238
     239                len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld) ", (long int)getpid() ); // use UNIX pid (versus getPid)
     240                __cfaabi_bits_write( STDERR_FILENO, abort_text, len );
     241
     242                assert( fmt );
     243                len = vsnprintf( abort_text, abort_text_size, fmt, args );
     244                __cfaabi_bits_write( STDERR_FILENO, abort_text, len );
     245
     246                // add optional newline if missing at the end of the format text
     247                if ( fmt[strlen( fmt ) - 1] != '\n' ) {
     248                        __cfaabi_bits_write( STDERR_FILENO, "\n", 1 );
     249                } // if
     250                kernel_abort_msg( kernel_data, abort_text, abort_text_size );
     251        }
     252
     253        // Second stage: print the backtrace
     254        if(stage == 2) {
     255                // increment stage
     256                stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST );
     257
     258                // print stack trace in handler
     259                __cfaabi_backtrace( signalAbort ? 4 : 2 );
     260        }
     261
     262        do {
     263                // Finally call abort
     264                __cabi_libc.abort();
     265
     266                // Loop so that we never return
     267        } while(true);
    245268}
    246269
     
    248271        va_list args;
    249272        va_start( args, fmt );
    250         abort( false, fmt, args );
     273        __abort( false, fmt, args );
     274    // CONTROL NEVER REACHES HERE!
    251275        va_end( args );
     276}
     277
     278void abort( bool signalAbort, const char fmt[], ... ) {
     279    va_list args;
     280    va_start( args, fmt );
     281    __abort( signalAbort, fmt, args );
     282    // CONTROL NEVER REACHES HERE!
     283    va_end( args );
    252284}
    253285
  • libcfa/src/iostream.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Feb 20 15:53:23 2020
    13 // Update Count     : 829
     12// Last Modified On : Mon Aug 24 08:31:35 2020
     13// Update Count     : 1130
    1414//
    1515
    1616#include "iostream.hfa"
    1717
    18 extern "C" {
    1918#include <stdio.h>
    2019#include <stdbool.h>                                                                    // true/false
    2120#include <stdint.h>                                                                             // UINT64_MAX
    22 //#include <string.h>                                                                   // strlen, strcmp
     21#include <float.h>                                                                              // DBL_DIG, LDBL_DIG
     22#include <complex.h>                                                                    // creal, cimag
     23//#include <string.h>                                                                   // strlen, strcmp, memcpy
     24extern "C" {
    2325extern size_t strlen (const char *__s) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
    2426extern int strcmp (const char *__s1, const char *__s2) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
    2527extern char *strcpy (char *__restrict __dest, const char *__restrict __src) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
    2628extern void *memcpy (void *__restrict __dest, const void *__restrict __src, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
    27 #include <float.h>                                                                              // DBL_DIG, LDBL_DIG
    28 #include <math.h>                                                                               // isfinite
    29 #include <complex.h>                                                                    // creal, cimag
    3029} // extern "C"
    3130
    32 
    33 //*********************************** ostream ***********************************
     31#include "math.hfa"                                                                             // isfinite, floor, ceiling_div
     32#include "bitmanip.hfa"                                                                 // high1
     33
     34
     35// *********************************** ostream ***********************************
    3436
    3537
    3638forall( dtype ostype | ostream( ostype ) ) {
    37         ostype & ?|?( ostype & os, zero_t ) {
    38                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
    39                 fmt( os, "%d", 0n );
    40                 return os;
    41         } // ?|?
    42         void ?|?( ostype & os, zero_t z ) {
    43                 (ostype &)(os | z); ends( os );
    44         } // ?|?
    45 
    46         ostype & ?|?( ostype & os, one_t ) {
    47                 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
    48                 fmt( os, "%d", 1n );
    49                 return os;
    50         } // ?|?
    51         void ?|?( ostype & os, one_t o ) {
    52                 (ostype &)(os | o); ends( os );
    53         } // ?|?
    54 
    5539        ostype & ?|?( ostype & os, bool b ) {
    5640                if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );
     
    165149        #define P10_UINT64 10_000_000_000_000_000_000_ULL       // 19 zeroes
    166150
    167         static void base10_128( ostype & os, unsigned int128 val ) {
    168                 if ( val > UINT64_MAX ) {
     151        static inline void base10_128( ostype & os, unsigned int128 val ) {
     152#if defined(__GNUC__) && __GNUC_PREREQ(7,0)                             // gcc version >= 7
     153                if ( val > P10_UINT64 ) {
     154#else
     155                if ( (uint64_t)(val >> 64) != 0 || (uint64_t)val > P10_UINT64 ) { // patch gcc 5 & 6 -O3 bug
     156#endif // __GNUC_PREREQ(7,0)
    169157                        base10_128( os, val / P10_UINT64 );                     // recursive
    170158                        fmt( os, "%.19lu", (uint64_t)(val % P10_UINT64) );
     
    174162        } // base10_128
    175163
    176         static void base10_128( ostype & os, int128 val ) {
     164        static inline void base10_128( ostype & os, int128 val ) {
    177165                if ( val < 0 ) {
    178166                        fmt( os, "-" );                                                         // leading negative sign
     
    445433} // distribution
    446434
    447 //*********************************** manipulators ***********************************
    448 
    449 //*********************************** integral ***********************************
     435// *********************************** manipulators ***********************************
     436
     437// *********************************** integral ***********************************
    450438
    451439static const char * shortbin[] = { "0", "1", "10", "11", "100", "101", "110", "111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
     
    453441
    454442// Default prefix for non-decimal prints is 0b, 0, 0x.
    455 #define IntegralFMTImpl( T, CODE, IFMTNP, IFMTP ) \
     443#define IntegralFMTImpl( T, IFMTNP, IFMTP ) \
    456444forall( dtype ostype | ostream( ostype ) ) { \
    457445        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
     
    459447\
    460448                if ( f.base == 'b' || f.base == 'B' ) {                 /* bespoke binary format */ \
    461                         int bits;                                                                                                       \
    462                         if ( f.val == (T){0} ) bits = 1;                        /* force at least one bit to print */ \
    463                         else bits = sizeof(long long int) * 8 - __builtin_clzll( f.val ); /* position of most significant bit */ \
    464                         bits = bits > sizeof(f.val) * 8 ? sizeof(f.val) * 8 : bits; \
    465                         int spaces = f.wd - bits;                                       /* can be negative */ \
    466                         if ( ! f.flags.nobsdp ) { spaces -= 2; }        /* base prefix takes space */ \
    467                         /* printf( "%d %d\n", bits, spaces ); */ \
     449                        int bits = high1( f.val );                                      /* position of most significant bit */ \
     450                        if ( bits == 0 ) bits = 1;                                      /* 0 value => force one bit to print */ \
     451                        int spaces; \
    468452                        if ( ! f.flags.left ) {                                         /* right justified ? */ \
    469453                                /* Note, base prefix then zero padding or spacing then prefix. */ \
    470                                 if ( f.flags.pad0 || f.flags.pc ) { \
     454                                if ( f.flags.pc ) { \
     455                                        spaces = f.wd - f.pc; \
     456                                        if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
     457                                        if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \
    471458                                        if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
    472                                         if ( f.flags.pc ) spaces = f.pc - bits; \
     459                                        spaces = f.pc - bits; \
    473460                                        if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
    474461                                } else { \
    475                                         if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \
    476                                         if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
     462                                        spaces = f.wd - bits; \
     463                                        if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
     464                                        if ( f.flags.pad0 ) { \
     465                                                if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
     466                                                if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
     467                                        } else { \
     468                                                if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \
     469                                                if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
     470                                        } /* if */ \
    477471                                } /* if */ \
    478                         } else if ( ! f.flags.nobsdp ) { \
    479                                 fmt( os, "0%c", f.base ); \
     472                        } else { \
     473                                if ( ! f.flags.nobsdp ) fmt( os, "0%c", f.base ); \
     474                                if ( f.flags.pc ) { \
     475                                        spaces = f.pc - bits; \
     476                                        if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
     477                                        spaces = f.wd - f.pc; \
     478                                } else { /* pad0 flag ignored with left flag */ \
     479                                        spaces = f.wd - bits; \
     480                                } /* if */ \
     481                                if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
    480482                        } /* if */ \
    481                         int shift = (bits - 1) / 4 * 4; /* floor( bits - 1, 4 ) */ \
     483                        int shift = floor( bits - 1, 4 ); \
    482484                        typeof( f.val ) temp = f.val; \
    483485                        fmt( os, "%s", shortbin[(temp >> shift) & 0xf] ); \
     
    490492                        if ( f.flags.left && spaces > 0 ) fmt( os, "%*s", spaces, " " ); \
    491493                        return os; \
    492                 } /* if  */ \
     494                } /* if */ \
    493495\
    494496                char fmtstr[sizeof(IFMTP)];                                             /* sizeof includes '\0' */ \
     
    500502                if ( ! f.flags.nobsdp ) { fmtstr[star] = '#'; star -= 1; } \
    501503                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \
    502                 if ( f.flags.sign && f.base == CODE ) { fmtstr[star] = '+'; star -= 1; } \
     504                if ( f.flags.sign ) { fmtstr[star] = '+'; star -= 1; } \
    503505                if ( f.flags.pad0 && ! f.flags.pc ) { fmtstr[star] = '0'; star -= 1; } \
    504506                fmtstr[star] = '%'; \
     
    506508                if ( ! f.flags.pc ) {                                                   /* no precision */ \
    507509                        fmtstr[sizeof(IFMTNP)-2] = f.base;                      /* sizeof includes '\0' */ \
    508                         /* printf( "%s %c %c\n", &fmtstr[star], f.base, CODE ); */ \
     510                        /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \
    509511                        fmt( os, &fmtstr[star], f.wd, f.val ); \
    510512                } else {                                                                                /* precision */ \
    511513                        fmtstr[sizeof(IFMTP)-2] = f.base;                       /* sizeof includes '\0' */ \
    512                         /* printf( "%s %c %c\n", &fmtstr[star], f.base, CODE ); */ \
     514                        /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \
    513515                        fmt( os, &fmtstr[star], f.wd, f.pc, f.val ); \
    514516                } /* if */ \
     
    518520} // distribution
    519521
    520 IntegralFMTImpl( signed char, 'd', "%    *hh ", "%    *.*hh " )
    521 IntegralFMTImpl( unsigned char, 'u', "%    *hh ", "%    *.*hh " )
    522 IntegralFMTImpl( signed short int, 'd', "%    *h ", "%    *.*h " )
    523 IntegralFMTImpl( unsigned short int, 'u', "%    *h ", "%    *.*h " )
    524 IntegralFMTImpl( signed int, 'd', "%    * ", "%    *.* " )
    525 IntegralFMTImpl( unsigned int, 'u', "%    * ", "%    *.* " )
    526 IntegralFMTImpl( signed long int, 'd', "%    *l ", "%    *.*l " )
    527 IntegralFMTImpl( unsigned long int, 'u', "%    *l ", "%    *.*l " )
    528 IntegralFMTImpl( signed long long int, 'd', "%    *ll ", "%    *.*ll " )
    529 IntegralFMTImpl( unsigned long long int, 'u', "%    *ll ", "%    *.*ll " )
    530 
    531 
     522IntegralFMTImpl( signed char, "%    *hh ", "%    *.*hh " )
     523IntegralFMTImpl( unsigned char, "%    *hh ", "%    *.*hh " )
     524IntegralFMTImpl( signed short int, "%    *h ", "%    *.*h " )
     525IntegralFMTImpl( unsigned short int, "%    *h ", "%    *.*h " )
     526IntegralFMTImpl( signed int, "%    * ", "%    *.* " )
     527IntegralFMTImpl( unsigned int, "%    * ", "%    *.* " )
     528IntegralFMTImpl( signed long int, "%    *l ", "%    *.*l " )
     529IntegralFMTImpl( unsigned long int, "%    *l ", "%    *.*l " )
     530IntegralFMTImpl( signed long long int, "%    *ll ", "%    *.*ll " )
     531IntegralFMTImpl( unsigned long long int, "%    *ll ", "%    *.*ll " )
     532
     533#if 0
    532534#if defined( __SIZEOF_INT128__ )
    533535// Default prefix for non-decimal prints is 0b, 0, 0x.
    534536#define IntegralFMTImpl128( T, SIGNED, CODE, IFMTNP, IFMTP ) \
    535537forall( dtype ostype | ostream( ostype ) ) \
    536 static void base10_128( ostype & os, _Ostream_Manip(T) fmt ) { \
    537         if ( fmt.val > UINT64_MAX ) { \
    538                 fmt.val /= P10_UINT64; \
    539                 base10_128( os, fmt ); /* recursive */ \
    540                 _Ostream_Manip(unsigned long long int) fmt2 @= { (uint64_t)(fmt.val % P10_UINT64), 0, 19, 'u', { .all : 0 } }; \
    541                 fmt2.flags.nobsdp = true; \
    542                 printf( "fmt2 %c %lld %d\n", fmt2.base, fmt2.val, fmt2.all );   \
     538static void base10_128( ostype & os, _Ostream_Manip(T) f ) { \
     539        if ( f.val > UINT64_MAX ) { \
     540                unsigned long long int lsig = f.val % P10_UINT64; \
     541                f.val /= P10_UINT64; /* msig */ \
     542                base10_128( os, f ); /* recursion */ \
     543                _Ostream_Manip(unsigned long long int) fmt @= { lsig, 0, 19, 'u', { .all : 0 } }; \
     544                fmt.flags.nobsdp = true; \
     545                /* printf( "fmt1 %c %lld %d\n", fmt.base, fmt.val, fmt.all ); */ \
    543546                sepOff( os ); \
    544                 (ostype &)(os | fmt2); \
     547                (ostype &)(os | fmt); \
    545548        } else { \
    546                 printf( "fmt %c %lld %d\n", fmt.base, fmt.val, fmt.all ); \
     549                /* printf( "fmt2 %c %lld %d\n", f.base, (unsigned long long int)f.val, f.all ); */ \
     550                _Ostream_Manip(SIGNED long long int) fmt @= { (SIGNED long long int)f.val, f.wd, f.pc, f.base, { .all : f.all } }; \
    547551                (ostype &)(os | fmt); \
    548552        } /* if */ \
    549 } /* base10_128 */                                                \
     553} /* base10_128 */ \
    550554forall( dtype ostype | ostream( ostype ) ) { \
    551555        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
    552556                if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \
    553557\
    554                 if ( f.base == 'b' | f.base == 'o' | f.base == 'x' | f.base == 'X' ) { \
     558                if ( f.base == 'b' | f.base == 'B' | f.base == 'o' | f.base == 'x' | f.base == 'X' ) { \
    555559                        unsigned long long int msig = (unsigned long long int)(f.val >> 64); \
    556560                        unsigned long long int lsig = (unsigned long long int)(f.val); \
     
    562566                        } else { \
    563567                                fmt2.flags.pad0 = fmt2.flags.nobsdp = true;     \
    564                                 if ( f.base == 'b' ) { \
    565                                         if ( f.wd > 64 ) fmt.wd = f.wd - 64; \
    566                                         fmt2.wd = 64; \
     568                                if ( f.base == 'b' | f.base == 'B' ) { \
     569                                        if ( fmt.flags.pc && fmt.pc > 64 ) fmt.pc -= 64; else { fmt.flags.pc = false; fmt.pc = 0; } \
     570                                        if ( fmt.flags.left ) { \
     571                                                fmt.flags.left = false; \
     572                                                fmt.wd = 0; \
     573                                                /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \
     574                                                fmt2.flags.left = true; \
     575                                                int msigd = high1( msig ); \
     576                                                fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \
     577                                                if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0b base specifier */ \
     578                                                if ( (int)fmt2.wd < 64 ) fmt2.wd = 64; /* cast deals with negative value */ \
     579                                                fmt2.flags.pc = true; fmt2.pc = 64; \
     580                                        } else { \
     581                                                if ( fmt.wd > 64 ) fmt.wd -= 64; \
     582                                                else fmt.wd = 1; \
     583                                                /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \
     584                                                fmt2.wd = 64; \
     585                                        } /* if */ \
     586                                        /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
    567587                                        (ostype &)(os | fmt | "" | fmt2); \
    568588                                } else if ( f.base == 'o' ) { \
     589                                        if ( fmt.flags.pc && fmt.pc > 22 ) fmt.pc -= 22; else { fmt.flags.pc = false; fmt.pc = 0; } \
    569590                                        fmt.val = (unsigned long long int)fmt.val >> 2; \
    570                                         if ( f.wd > 21 ) fmt.wd = f.wd - 21; \
    571                                         fmt2.wd = 1; \
    572                                         fmt2.val = ((msig & 0x3) << 1) + 1; \
    573                                         (ostype &)(os | fmt | "" | fmt2); \
    574                                         sepOff( os ); \
    575                                         fmt2.wd = 21; \
    576                                         fmt2.val = lsig & 0x7fffffffffffffff; \
     591                                        fmt2.val = ((msig & 0x3) << 1) + ((lsig & 0x8000000000000000U) != 0); \
     592                                        if ( fmt.flags.left ) { \
     593                                                fmt.flags.left = false; \
     594                                                fmt.wd = 0; \
     595                                                /* printf( "L %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
     596                                                (ostype &)(os | fmt | "" | fmt2); \
     597                                                sepOff( os ); \
     598                                                fmt2.flags.left = true; \
     599                                                int msigd = ceiling_div( high1( fmt.val ), 3 ); \
     600                                                fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \
     601                                                if ( ! fmt.flags.nobsdp ) fmt2.wd -= 1; /* compensate for 0 base specifier */ \
     602                                                if ( (int)fmt2.wd < 21 ) fmt2.wd = 21; /* cast deals with negative value */ \
     603                                                fmt2.flags.pc = true; fmt2.pc = 21; \
     604                                        } else { \
     605                                                if ( fmt.wd > 22 ) fmt.wd -= 22; \
     606                                                else fmt.wd = 1; \
     607                                                /* printf( "R %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
     608                                                (ostype &)(os | fmt | "" | fmt2); \
     609                                                sepOff( os ); \
     610                                                fmt2.wd = 21; \
     611                                        } /* if */ \
     612                                        fmt2.val = lsig & 0x7fffffffffffffffU; \
     613                                        /* printf( "\nC %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
    577614                                        (ostype &)(os | fmt2); \
    578                                 } else { \
    579                                         if ( f.flags.left ) { \
    580                                                 if ( f.wd > 16 ) fmt2.wd = f.wd - 16;   \
    581                                                 fmt.wd = 16;                                                    \
     615                                } else { /* f.base == 'x'  | f.base == 'X' */ \
     616                                        if ( fmt.flags.pc && fmt.pc > 16 ) fmt.pc -= 16; else { fmt.flags.pc = false; fmt.pc = 0; } \
     617                                        if ( fmt.flags.left ) { \
     618                                                fmt.flags.left = false; \
     619                                                fmt.wd = 0; \
     620                                                /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \
     621                                                fmt2.flags.left = true; \
     622                                                int msigd = high1( msig ); \
     623                                                fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \
     624                                                if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0x base specifier */ \
     625                                                if ( (int)fmt2.wd < 16 ) fmt2.wd = 16; /* cast deals with negative value */ \
     626                                                fmt2.flags.pc = true; fmt2.pc = 16; \
    582627                                        } else { \
    583                                                 if ( f.wd > 16 ) fmt.wd = f.wd - 16;    \
    584                                                 fmt2.wd = 16;                                                   \
     628                                                if ( fmt.wd > 16 ) fmt.wd -= 16; \
     629                                                else fmt.wd = 1; \
     630                                                /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \
     631                                                fmt2.wd = 16; \
    585632                                        } /* if */ \
     633                                        /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
    586634                                        (ostype &)(os | fmt | "" | fmt2); \
    587635                                } /* if */ \
    588636                        } /* if */ \
    589637                } else { \
     638                        if ( CODE == 'd' ) { \
     639                                if ( f.val < 0 )  { fmt( os, "-" ); sepOff( os ); f.val = -f.val; f.flags.sign = false; } \
     640                        } /* if */ \
    590641                        base10_128( os, f ); \
    591642                } /* if */ \
     
    598649IntegralFMTImpl128( unsigned int128, unsigned, 'u', "%    *ll ", "%    *.*ll " )
    599650#endif // __SIZEOF_INT128__
    600 
    601 //*********************************** floating point ***********************************
     651#endif // 0
     652
     653#if 1
     654#if defined( __SIZEOF_INT128__ )
     655// Default prefix for non-decimal prints is 0b, 0, 0x.
     656forall( dtype ostype | ostream( ostype ) )
     657static inline void base_128( ostype & os, unsigned int128 val, unsigned int128 power, _Ostream_Manip(uint64_t) & f, unsigned int maxdig, unsigned int bits, unsigned int cnt = 0 ) {
     658        int wd = 1;                                                                                     // f.wd is never 0 because 0 implies left-pad
     659        if ( val > power ) {                                                            // subdivide value into printable 64-bit values
     660                base_128( os, val / power, power, f, maxdig, bits, cnt + 1 ); // recursive
     661                f.val = val % power;
     662                if ( cnt == 1 && f.flags.left ) { wd = f.wd; f.wd = maxdig; } // copy f.wd and reset for printing middle chunk
     663                // printf( "R val:%#lx(%lu) wd:%u pc:%u base:%c neg:%d pc:%d left:%d nobsdp:%d sign:%d pad0:%d\n",
     664                //              f.val, f.val, f.wd, f.pc, f.base, f.flags.neg, f.flags.pc, f.flags.left, f.flags.nobsdp, f.flags.sign, f.flags.pad0 );
     665                (ostype &)(os | f);
     666                if ( cnt == 1 ) {
     667                        if ( f.flags.left ) { wd -= maxdig; f.wd = wd < 0 ? 1 : wd; } // update and restore f.wd for printing end chunk
     668                        sepOff( os );                                                           // no seperator between chunks
     669                } // if
     670        } else {                                                                                        // print start chunk
     671                f.val = val;
     672                // f.pc is unsigned => use wd
     673                if ( f.flags.pc && f.pc > maxdig * cnt ) { wd = f.pc - maxdig * cnt; f.pc = wd < 0 ? 0 : wd; }
     674                else { f.flags.pc = false; f.pc = 0; }
     675
     676                if ( ! f.flags.left ) {                                                 // right justify
     677                        wd = f.wd - maxdig * cnt;
     678                        f.wd = wd < 0 ? 1 : wd;
     679                        wd = maxdig;
     680                } else {                                                                                // left justify
     681                        if ( cnt != 0 ) {                                                       // value >= 2^64 ?
     682                                unsigned int dig, bs = 0;
     683                                // compute size of prefix digits and base
     684                                if ( f.base == 'd' || f.base == 'u' ) { // no base prefix
     685                                        dig = ceil( log10( f.val ) );           // use floating-point
     686                                        if ( f.base == 'd' && (f.flags.neg || f.flags.sign) ) bs = 1; // sign ?
     687                                } else {
     688                                        dig = ceiling_div( high1( f.val ), bits );
     689                                        if ( ! f.flags.nobsdp ) {                       // base prefix ?
     690                                                if ( f.base == 'o' ) {
     691                                                        // 0 prefix for octal is not added for precision with leading zero
     692                                                        if ( f.pc <= dig ) bs = 1;      // 1 character prefix
     693                                                } else bs = 2;                                  // 2 character prefix
     694                                        } // if
     695                                } // if
     696                                wd = f.wd - (f.pc > dig ? f.pc : dig) - bs; // precision > leading digits ?
     697                                if ( wd < 0 ) wd = 1;
     698                                f.wd = 1;
     699                        } // if
     700                        // all manipulators handled implicitly for value < 2^64
     701                } // if
     702                // prior checks ensure wd not negative
     703
     704                if ( f.flags.neg ) f.val = -f.val;
     705                // printf( "L val:%#lx(%lu) wd:%u pc:%u base:%c neg:%d pc:%d left:%d nobsdp:%d sign:%d pad0:%d\n",
     706                //              f.val, f.val, f.wd, f.pc, f.base, f.flags.neg, f.flags.pc, f.flags.left, f.flags.nobsdp, f.flags.sign, f.flags.pad0 );
     707                (ostype &)(os | f);
     708
     709                // remaining middle and end chunks are padded with 0s on the left
     710                if ( ! f.flags.left ) { f.flags.pad0 = true; f.flags.pc = false; } // left pad with 0s
     711                else { f.pc = maxdig; f.flags.pc = true; }              // left pad with precision
     712
     713                if ( cnt != 0 ) sepOff( os );                                   // no seperator between chunks
     714                f.wd = wd;                                                                              // reset f.wd for next chunk
     715                f.flags.sign = false;                                                   // no leading +/- sign
     716                f.flags.nobsdp = true;                                                  // no leading base prefix
     717        } // if
     718} // base_128
     719
     720#define IntegralFMTImpl128( T ) \
     721forall( dtype ostype | ostream( ostype ) ) { \
     722        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
     723                _Ostream_Manip(uint64_t) fmt; \
     724                fmt.[wd, pc, base, all] = f.[wd, pc, base, all]; \
     725                if ( f.base == 'b' | f.base == 'B' ) { \
     726                        base_128( os, f.val, (unsigned int128)1 << 64, fmt, 64, 1 ); \
     727                } else if ( f.base == 'o' ) { \
     728                        base_128( os, f.val, (unsigned int128)1 << 63, fmt, 21, 3 ); \
     729                } else if ( f.base == 'd' || f.base == 'u' ) { \
     730                        if ( f.base == 'd' && f.val < 0 ) { f.val = -f.val; fmt.flags.neg = true; } \
     731                        base_128( os, f.val, (unsigned int128)10_000_000_000_000_000_000UL, fmt, 19, 0 ); \
     732                } else { \
     733                        base_128( os, f.val, (unsigned int128)1 << 64, fmt, 16, 4 ); \
     734                } /* if */ \
     735                return os; \
     736        } /* ?|? */ \
     737        void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \
     738} // distribution
     739
     740IntegralFMTImpl128( int128 )
     741IntegralFMTImpl128( unsigned int128 )
     742#endif // __SIZEOF_INT128__
     743#endif // 0
     744
     745// *********************************** floating point ***********************************
    602746
    603747#define PrintWithDP2( os, format, val, ... ) \
     
    655799FloatingPointFMTImpl( long double, "%    *L ", "%    *.*L " )
    656800
    657 //*********************************** character ***********************************
     801// *********************************** character ***********************************
    658802
    659803forall( dtype ostype | ostream( ostype ) ) {
     
    688832} // distribution
    689833
    690 //*********************************** C string ***********************************
     834// *********************************** C string ***********************************
    691835
    692836forall( dtype ostype | ostream( ostype ) ) {
     
    735879
    736880
    737 //*********************************** istream ***********************************
     881// *********************************** istream ***********************************
    738882
    739883
     
    812956        } // ?|?
    813957
     958#if defined( __SIZEOF_INT128__ )
     959        istype & ?|?( istype & is, int128 & i128 ) {
     960                return (istype &)(is | (unsigned int128 &)i128);
     961        } // ?|?
     962
     963        istype & ?|?( istype & is, unsigned int128 & ui128 ) {
     964                char s[40];
     965                bool sign = false;
     966
     967                if ( fmt( is, " %[-]", s ) == 1 ) sign = true;  // skip whitespace, negative sign ?
     968                // If the input is too large, the value returned is undefined. If there is no input, no value is returned
     969                if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) {   // take first 39 characters, ignore remaining
     970                        ui128 = 0;
     971                        for ( unsigned int i = 0; s[i] != '\0'; i += 1 ) {
     972                                ui128 = ui128 * 10 + s[i] - '0';
     973                        } // for
     974                        if ( sign ) ui128 = -ui128;
     975                } else if ( sign ) ungetc( is, '-' );                   // return minus when no digits
     976                return is;
     977        } // ?|?
     978#endif // __SIZEOF_INT128__
    814979
    815980        istype & ?|?( istype & is, float & f ) {
     
    8811046} // distribution
    8821047
    883 //*********************************** manipulators ***********************************
     1048// *********************************** manipulators ***********************************
    8841049
    8851050forall( dtype istype | istream( istype ) )
  • libcfa/src/iostream.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Feb 20 15:30:56 2020
    13 // Update Count     : 337
     12// Last Modified On : Tue Aug 11 22:16:14 2020
     13// Update Count     : 350
    1414//
    1515
     
    1919
    2020
    21 //*********************************** ostream ***********************************
     21// *********************************** ostream ***********************************
    2222
    2323
     
    6767
    6868forall( dtype ostype | ostream( ostype ) ) {
    69         ostype & ?|?( ostype &, zero_t );
    70         void ?|?( ostype &, zero_t );
    71         ostype & ?|?( ostype &, one_t );
    72         void ?|?( ostype &, one_t );
    73 
    7469        ostype & ?|?( ostype &, bool );
    7570        void ?|?( ostype &, bool );
     
    156151} // distribution
    157152
    158 //*********************************** manipulators ***********************************
     153// *********************************** manipulators ***********************************
    159154
    160155forall( otype T )
     
    166161                unsigned char all;
    167162                struct {
     163                        unsigned char neg:1;                                            // val is negative
    168164                        unsigned char pc:1;                                                     // precision specified
    169165                        unsigned char left:1;                                           // left justify
     
    175171}; // _Ostream_Manip
    176172
    177 //*********************************** integral ***********************************
     173// *********************************** integral ***********************************
    178174
    179175// See 6.7.9. 19) The initialization shall occur in initializer list order, each initializer provided for a particular
     
    215211IntegralFMTDecl( int128, 'd' )
    216212IntegralFMTDecl( unsigned int128, 'u' )
    217 #endif
    218 
    219 //*********************************** floating point ***********************************
     213#endif // __SIZEOF_INT128__
     214
     215// *********************************** floating point ***********************************
    220216
    221217// Default suffix for values with no fraction is "."
     
    246242FloatingPointFMTDecl( long double )
    247243
    248 //*********************************** character ***********************************
     244// *********************************** character ***********************************
    249245
    250246static inline {
     
    263259} // ?|?
    264260
    265 //*********************************** C string ***********************************
     261// *********************************** C string ***********************************
    266262
    267263static inline {
     
    282278
    283279
    284 //*********************************** istream ***********************************
     280// *********************************** istream ***********************************
    285281
    286282
     
    314310        istype & ?|?( istype &, unsigned int & );
    315311        istype & ?|?( istype &, long int & );
     312        istype & ?|?( istype &, unsigned long int & );
    316313        istype & ?|?( istype &, long long int & );
    317         istype & ?|?( istype &, unsigned long int & );
    318314        istype & ?|?( istype &, unsigned long long int & );
     315#if defined( __SIZEOF_INT128__ )
     316        istype & ?|?( istype &, int128 & );
     317        istype & ?|?( istype &, unsigned int128 & );
     318#endif // __SIZEOF_INT128__
    319319
    320320        istype & ?|?( istype &, float & );
     
    336336} // distribution
    337337
    338 //*********************************** manipulators ***********************************
     338// *********************************** manipulators ***********************************
    339339
    340340struct _Istream_Cstr {
     
    358358        _Istream_Cstr excl( const char scanset[], char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : true } }; }
    359359        _Istream_Cstr & excl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; }
    360         _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cstr)@{ s, 0p, -1, { .flags.ignore : true } }; }
     360        _Istream_Cstr ignore( char s[] ) { return (_Istream_Cstr)@{ s, 0p, -1, { .flags.ignore : true } }; }
    361361        _Istream_Cstr & ignore( _Istream_Cstr & fmt ) { fmt.flags.ignore = true; return fmt; }
    362362        _Istream_Cstr wdi( unsigned int w, char s[] ) { return (_Istream_Cstr)@{ s, 0p, w, { .all : 0 } }; }
     
    370370
    371371static inline {
    372         _Istream_Char ignore( const char c ) { return (_Istream_Char)@{ true }; }
     372        _Istream_Char ignore( const char ) { return (_Istream_Char)@{ true }; }
    373373        _Istream_Char & ignore( _Istream_Char & fmt ) { fmt.ignore = true; return fmt; }
    374374} // distribution
    375375forall( dtype istype | istream( istype ) ) istype & ?|?( istype & is, _Istream_Char f );
    376376
    377 forall( otype T )
     377forall( dtype T | sized( T ) )
    378378struct _Istream_Manip {
    379379        T & val;                                                                                        // polymorphic base-type
     
    413413
    414414
    415 //*********************************** time ***********************************
     415// *********************************** time ***********************************
    416416
    417417
  • libcfa/src/math.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Mon Apr 18 23:37:04 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 10:27:11 2020
    13 // Update Count     : 117
     12// Last Modified On : Mon Aug 24 08:56:20 2020
     13// Update Count     : 126
    1414//
    1515
     
    1919#include <complex.h>
    2020
     21//---------------------------------------
     22
     23#include "common.hfa"
     24
    2125//---------------------- General ----------------------
    2226
    23 static inline float ?%?( float x, float y ) { return fmodf( x, y ); }
    24 static inline float fmod( float x, float y ) { return fmodf( x, y ); }
    25 static inline double ?%?( double x, double y ) { return fmod( x, y ); }
    26 // extern "C" { double fmod( double, double ); }
    27 static inline long double ?%?( long double x, long double y ) { return fmodl( x, y ); }
    28 static inline long double fmod( long double x, long double y ) { return fmodl( x, y ); }
    29 
    30 static inline float remainder( float x, float y ) { return remainderf( x, y ); }
    31 // extern "C" { double remainder( double, double ); }
    32 static inline long double remainder( long double x, long double y ) { return remainderl( x, y ); }
    33 
    34 static inline float remquo( float x, float y, int * quo ) { return remquof( x, y, quo ); }
    35 // extern "C" { double remquo( double x, double y, int * quo ); }
    36 static inline long double remquo( long double x, long double y, int * quo ) { return remquol( x, y, quo ); }
    37 static inline [ int, float ] remquo( float x, float y ) { int quo; x = remquof( x, y, &quo ); return [ quo, x ]; }
    38 static inline [ int, double ] remquo( double x, double y ) { int quo; x = remquo( x, y, &quo ); return [ quo, x ]; }
    39 static inline [ int, long double ] remquo( long double x, long double y ) { int quo; x = remquol( x, y, &quo ); return [ quo, x ]; }
    40 
    41 static inline [ float, float ] div( float x, float y ) { y = modff( x / y, &x ); return [ x, y ]; }
    42 static inline [ double, double ] div( double x, double y ) { y = modf( x / y, &x ); return [ x, y ]; }
    43 static inline [ long double, long double ] div( long double x, long double y ) { y = modfl( x / y, &x ); return [ x, y ]; }
    44 
    45 static inline float fma( float x, float y, float z ) { return fmaf( x, y, z ); }
    46 // extern "C" { double fma( double, double, double ); }
    47 static inline long double fma( long double x, long double y, long double z ) { return fmal( x, y, z ); }
    48 
    49 static inline float fdim( float x, float y ) { return fdimf( x, y ); }
    50 // extern "C" { double fdim( double, double ); }
    51 static inline long double fdim( long double x, long double y ) { return fdiml( x, y ); }
    52 
    53 static inline float nan( const char tag[] ) { return nanf( tag ); }
    54 // extern "C" { double nan( const char [] ); }
    55 static inline long double nan( const char tag[] ) { return nanl( tag ); }
     27static inline {
     28        float ?%?( float x, float y ) { return fmodf( x, y ); }
     29        float fmod( float x, float y ) { return fmodf( x, y ); }
     30        double ?%?( double x, double y ) { return fmod( x, y ); }
     31        // extern "C" { double fmod( double, double ); }
     32        long double ?%?( long double x, long double y ) { return fmodl( x, y ); }
     33        long double fmod( long double x, long double y ) { return fmodl( x, y ); }
     34
     35        float remainder( float x, float y ) { return remainderf( x, y ); }
     36        // extern "C" { double remainder( double, double ); }
     37        long double remainder( long double x, long double y ) { return remainderl( x, y ); }
     38
     39        float remquo( float x, float y, int * quo ) { return remquof( x, y, quo ); }
     40        // extern "C" { double remquo( double x, double y, int * quo ); }
     41        long double remquo( long double x, long double y, int * quo ) { return remquol( x, y, quo ); }
     42        [ int, float ] remquo( float x, float y ) { int quo; x = remquof( x, y, &quo ); return [ quo, x ]; }
     43        [ int, double ] remquo( double x, double y ) { int quo; x = remquo( x, y, &quo ); return [ quo, x ]; }
     44        [ int, long double ] remquo( long double x, long double y ) { int quo; x = remquol( x, y, &quo ); return [ quo, x ]; }
     45
     46        [ float, float ] div( float x, float y ) { y = modff( x / y, &x ); return [ x, y ]; }
     47        [ double, double ] div( double x, double y ) { y = modf( x / y, &x ); return [ x, y ]; }
     48        [ long double, long double ] div( long double x, long double y ) { y = modfl( x / y, &x ); return [ x, y ]; }
     49
     50        float fma( float x, float y, float z ) { return fmaf( x, y, z ); }
     51        // extern "C" { double fma( double, double, double ); }
     52        long double fma( long double x, long double y, long double z ) { return fmal( x, y, z ); }
     53
     54        float fdim( float x, float y ) { return fdimf( x, y ); }
     55        // extern "C" { double fdim( double, double ); }
     56        long double fdim( long double x, long double y ) { return fdiml( x, y ); }
     57
     58        float nan( const char tag[] ) { return nanf( tag ); }
     59        // extern "C" { double nan( const char [] ); }
     60        long double nan( const char tag[] ) { return nanl( tag ); }
     61} // distribution
    5662
    5763//---------------------- Exponential ----------------------
    5864
    59 static inline float exp( float x ) { return expf( x ); }
    60 // extern "C" { double exp( double ); }
    61 static inline long double exp( long double x ) { return expl( x ); }
    62 static inline float _Complex exp( float _Complex x ) { return cexpf( x ); }
    63 static inline double _Complex exp( double _Complex x ) { return cexp( x ); }
    64 static inline long double _Complex exp( long double _Complex x ) { return cexpl( x ); }
    65 
    66 static inline float exp2( float x ) { return exp2f( x ); }
    67 // extern "C" { double exp2( double ); }
    68 static inline long double exp2( long double x ) { return exp2l( x ); }
    69 //static inline float _Complex exp2( float _Complex x ) { return cexp2f( x ); }
    70 //static inline double _Complex exp2( double _Complex x ) { return cexp2( x ); }
    71 //static inline long double _Complex exp2( long double _Complex x ) { return cexp2l( x ); }
    72 
    73 static inline float expm1( float x ) { return expm1f( x ); }
    74 // extern "C" { double expm1( double ); }
    75 static inline long double expm1( long double x ) { return expm1l( x ); }
    76 
    77 static inline float pow( float x, float y ) { return powf( x, y ); }
    78 // extern "C" { double pow( double, double ); }
    79 static inline long double pow( long double x, long double y ) { return powl( x, y ); }
    80 static inline float _Complex pow( float _Complex x, float _Complex y ) { return cpowf( x, y ); }
    81 static inline double _Complex pow( double _Complex x, double _Complex y ) { return cpow( x, y ); }
    82 static inline long double _Complex pow( long double _Complex x, long double _Complex y ) { return cpowl( x, y ); }
     65static inline {
     66        float exp( float x ) { return expf( x ); }
     67        // extern "C" { double exp( double ); }
     68        long double exp( long double x ) { return expl( x ); }
     69        float _Complex exp( float _Complex x ) { return cexpf( x ); }
     70        double _Complex exp( double _Complex x ) { return cexp( x ); }
     71        long double _Complex exp( long double _Complex x ) { return cexpl( x ); }
     72
     73        float exp2( float x ) { return exp2f( x ); }
     74        // extern "C" { double exp2( double ); }
     75        long double exp2( long double x ) { return exp2l( x ); }
     76        //float _Complex exp2( float _Complex x ) { return cexp2f( x ); }
     77        //double _Complex exp2( double _Complex x ) { return cexp2( x ); }
     78        //long double _Complex exp2( long double _Complex x ) { return cexp2l( x ); }
     79
     80        float expm1( float x ) { return expm1f( x ); }
     81        // extern "C" { double expm1( double ); }
     82        long double expm1( long double x ) { return expm1l( x ); }
     83
     84        float pow( float x, float y ) { return powf( x, y ); }
     85        // extern "C" { double pow( double, double ); }
     86        long double pow( long double x, long double y ) { return powl( x, y ); }
     87        float _Complex pow( float _Complex x, float _Complex y ) { return cpowf( x, y ); }
     88        double _Complex pow( double _Complex x, double _Complex y ) { return cpow( x, y ); }
     89        long double _Complex pow( long double _Complex x, long double _Complex y ) { return cpowl( x, y ); }
     90} // distribution
    8391
    8492//---------------------- Logarithm ----------------------
    8593
    86 static inline float log( float x ) { return logf( x ); }
    87 // extern "C" { double log( double ); }
    88 static inline long double log( long double x ) { return logl( x ); }
    89 static inline float _Complex log( float _Complex x ) { return clogf( x ); }
    90 static inline double _Complex log( double _Complex x ) { return clog( x ); }
    91 static inline long double _Complex log( long double _Complex x ) { return clogl( x ); }
    92 
    93 static inline float log2( float x ) { return log2f( x ); }
    94 // extern "C" { double log2( double ); }
    95 static inline long double log2( long double x ) { return log2l( x ); }
    96 // static inline float _Complex log2( float _Complex x ) { return clog2f( x ); }
    97 // static inline double _Complex log2( double _Complex x ) { return clog2( x ); }
    98 // static inline long double _Complex log2( long double _Complex x ) { return clog2l( x ); }
    99 
    100 static inline float log10( float x ) { return log10f( x ); }
    101 // extern "C" { double log10( double ); }
    102 static inline long double log10( long double x ) { return log10l( x ); }
    103 // static inline float _Complex log10( float _Complex x ) { return clog10f( x ); }
    104 // static inline double _Complex log10( double _Complex x ) { return clog10( x ); }
    105 // static inline long double _Complex log10( long double _Complex x ) { return clog10l( x ); }
    106 
    107 static inline float log1p( float x ) { return log1pf( x ); }
    108 // extern "C" { double log1p( double ); }
    109 static inline long double log1p( long double x ) { return log1pl( x ); }
    110 
    111 static inline int ilogb( float x ) { return ilogbf( x ); }
    112 // extern "C" { int ilogb( double ); }
    113 static inline int ilogb( long double x ) { return ilogbl( x ); }
    114 
    115 static inline float logb( float x ) { return logbf( x ); }
    116 // extern "C" { double logb( double ); }
    117 static inline long double logb( long double x ) { return logbl( x ); }
    118 
    119 static inline float sqrt( float x ) { return sqrtf( x ); }
    120 // extern "C" { double sqrt( double ); }
    121 static inline long double sqrt( long double x ) { return sqrtl( x ); }
    122 static inline float _Complex sqrt( float _Complex x ) { return csqrtf( x ); }
    123 static inline double _Complex sqrt( double _Complex x ) { return csqrt( x ); }
    124 static inline long double _Complex sqrt( long double _Complex x ) { return csqrtl( x ); }
    125 
    126 static inline float cbrt( float x ) { return cbrtf( x ); }
    127 // extern "C" { double cbrt( double ); }
    128 static inline long double cbrt( long double x ) { return cbrtl( x ); }
    129 
    130 static inline float hypot( float x, float y ) { return hypotf( x, y ); }
    131 // extern "C" { double hypot( double, double ); }
    132 static inline long double hypot( long double x, long double y ) { return hypotl( x, y ); }
     94static inline {
     95        float log( float x ) { return logf( x ); }
     96        // extern "C" { double log( double ); }
     97        long double log( long double x ) { return logl( x ); }
     98        float _Complex log( float _Complex x ) { return clogf( x ); }
     99        double _Complex log( double _Complex x ) { return clog( x ); }
     100        long double _Complex log( long double _Complex x ) { return clogl( x ); }
     101
     102        float log2( float x ) { return log2f( x ); }
     103        // extern "C" { double log2( double ); }
     104        long double log2( long double x ) { return log2l( x ); }
     105        // float _Complex log2( float _Complex x ) { return clog2f( x ); }
     106        // double _Complex log2( double _Complex x ) { return clog2( x ); }
     107        // long double _Complex log2( long double _Complex x ) { return clog2l( x ); }
     108
     109        float log10( float x ) { return log10f( x ); }
     110        // extern "C" { double log10( double ); }
     111        long double log10( long double x ) { return log10l( x ); }
     112        // float _Complex log10( float _Complex x ) { return clog10f( x ); }
     113        // double _Complex log10( double _Complex x ) { return clog10( x ); }
     114        // long double _Complex log10( long double _Complex x ) { return clog10l( x ); }
     115
     116        float log1p( float x ) { return log1pf( x ); }
     117        // extern "C" { double log1p( double ); }
     118        long double log1p( long double x ) { return log1pl( x ); }
     119
     120        int ilogb( float x ) { return ilogbf( x ); }
     121        // extern "C" { int ilogb( double ); }
     122        int ilogb( long double x ) { return ilogbl( x ); }
     123
     124        float logb( float x ) { return logbf( x ); }
     125        // extern "C" { double logb( double ); }
     126        long double logb( long double x ) { return logbl( x ); }
     127
     128        float sqrt( float x ) { return sqrtf( x ); }
     129        // extern "C" { double sqrt( double ); }
     130        long double sqrt( long double x ) { return sqrtl( x ); }
     131        float _Complex sqrt( float _Complex x ) { return csqrtf( x ); }
     132        double _Complex sqrt( double _Complex x ) { return csqrt( x ); }
     133        long double _Complex sqrt( long double _Complex x ) { return csqrtl( x ); }
     134
     135        float cbrt( float x ) { return cbrtf( x ); }
     136        // extern "C" { double cbrt( double ); }
     137        long double cbrt( long double x ) { return cbrtl( x ); }
     138
     139        float hypot( float x, float y ) { return hypotf( x, y ); }
     140        // extern "C" { double hypot( double, double ); }
     141        long double hypot( long double x, long double y ) { return hypotl( x, y ); }
     142} // distribution
    133143
    134144//---------------------- Trigonometric ----------------------
    135145
    136 static inline float sin( float x ) { return sinf( x ); }
    137 // extern "C" { double sin( double ); }
    138 static inline long double sin( long double x ) { return sinl( x ); }
    139 static inline float _Complex sin( float _Complex x ) { return csinf( x ); }
    140 static inline double _Complex sin( double _Complex x ) { return csin( x ); }
    141 static inline long double _Complex sin( long double _Complex x ) { return csinl( x ); }
    142 
    143 static inline float cos( float x ) { return cosf( x ); }
    144 // extern "C" { double cos( double ); }
    145 static inline long double cos( long double x ) { return cosl( x ); }
    146 static inline float _Complex cos( float _Complex x ) { return ccosf( x ); }
    147 static inline double _Complex cos( double _Complex x ) { return ccos( x ); }
    148 static inline long double _Complex cos( long double _Complex x ) { return ccosl( x ); }
    149 
    150 static inline float tan( float x ) { return tanf( x ); }
    151 // extern "C" { double tan( double ); }
    152 static inline long double tan( long double x ) { return tanl( x ); }
    153 static inline float _Complex tan( float _Complex x ) { return ctanf( x ); }
    154 static inline double _Complex tan( double _Complex x ) { return ctan( x ); }
    155 static inline long double _Complex tan( long double _Complex x ) { return ctanl( x ); }
    156 
    157 static inline float asin( float x ) { return asinf( x ); }
    158 // extern "C" { double asin( double ); }
    159 static inline long double asin( long double x ) { return asinl( x ); }
    160 static inline float _Complex asin( float _Complex x ) { return casinf( x ); }
    161 static inline double _Complex asin( double _Complex x ) { return casin( x ); }
    162 static inline long double _Complex asin( long double _Complex x ) { return casinl( x ); }
    163 
    164 static inline float acos( float x ) { return acosf( x ); }
    165 // extern "C" { double acos( double ); }
    166 static inline long double acos( long double x ) { return acosl( x ); }
    167 static inline float _Complex acos( float _Complex x ) { return cacosf( x ); }
    168 static inline double _Complex acos( double _Complex x ) { return cacos( x ); }
    169 static inline long double _Complex acos( long double _Complex x ) { return cacosl( x ); }
    170 
    171 static inline float atan( float x ) { return atanf( x ); }
    172 // extern "C" { double atan( double ); }
    173 static inline long double atan( long double x ) { return atanl( x ); }
    174 static inline float _Complex atan( float _Complex x ) { return catanf( x ); }
    175 static inline double _Complex atan( double _Complex x ) { return catan( x ); }
    176 static inline long double _Complex atan( long double _Complex x ) { return catanl( x ); }
    177 
    178 static inline float atan2( float x, float y ) { return atan2f( x, y ); }
    179 // extern "C" { double atan2( double, double ); }
    180 static inline long double atan2( long double x, long double y ) { return atan2l( x, y ); }
    181 
    182 // alternative name for atan2
    183 static inline float atan( float x, float y ) { return atan2f( x, y ); }
    184 static inline double atan( double x, double y ) { return atan2( x, y ); }
    185 static inline long double atan( long double x, long double y ) { return atan2l( x, y ); }
     146static inline {
     147        float sin( float x ) { return sinf( x ); }
     148        // extern "C" { double sin( double ); }
     149        long double sin( long double x ) { return sinl( x ); }
     150        float _Complex sin( float _Complex x ) { return csinf( x ); }
     151        double _Complex sin( double _Complex x ) { return csin( x ); }
     152        long double _Complex sin( long double _Complex x ) { return csinl( x ); }
     153
     154        float cos( float x ) { return cosf( x ); }
     155        // extern "C" { double cos( double ); }
     156        long double cos( long double x ) { return cosl( x ); }
     157        float _Complex cos( float _Complex x ) { return ccosf( x ); }
     158        double _Complex cos( double _Complex x ) { return ccos( x ); }
     159        long double _Complex cos( long double _Complex x ) { return ccosl( x ); }
     160
     161        float tan( float x ) { return tanf( x ); }
     162        // extern "C" { double tan( double ); }
     163        long double tan( long double x ) { return tanl( x ); }
     164        float _Complex tan( float _Complex x ) { return ctanf( x ); }
     165        double _Complex tan( double _Complex x ) { return ctan( x ); }
     166        long double _Complex tan( long double _Complex x ) { return ctanl( x ); }
     167
     168        float asin( float x ) { return asinf( x ); }
     169        // extern "C" { double asin( double ); }
     170        long double asin( long double x ) { return asinl( x ); }
     171        float _Complex asin( float _Complex x ) { return casinf( x ); }
     172        double _Complex asin( double _Complex x ) { return casin( x ); }
     173        long double _Complex asin( long double _Complex x ) { return casinl( x ); }
     174
     175        float acos( float x ) { return acosf( x ); }
     176        // extern "C" { double acos( double ); }
     177        long double acos( long double x ) { return acosl( x ); }
     178        float _Complex acos( float _Complex x ) { return cacosf( x ); }
     179        double _Complex acos( double _Complex x ) { return cacos( x ); }
     180        long double _Complex acos( long double _Complex x ) { return cacosl( x ); }
     181
     182        float atan( float x ) { return atanf( x ); }
     183        // extern "C" { double atan( double ); }
     184        long double atan( long double x ) { return atanl( x ); }
     185        float _Complex atan( float _Complex x ) { return catanf( x ); }
     186        double _Complex atan( double _Complex x ) { return catan( x ); }
     187        long double _Complex atan( long double _Complex x ) { return catanl( x ); }
     188
     189        float atan2( float x, float y ) { return atan2f( x, y ); }
     190        // extern "C" { double atan2( double, double ); }
     191        long double atan2( long double x, long double y ) { return atan2l( x, y ); }
     192
     193        // alternative name for atan2
     194        float atan( float x, float y ) { return atan2f( x, y ); }
     195        double atan( double x, double y ) { return atan2( x, y ); }
     196        long double atan( long double x, long double y ) { return atan2l( x, y ); }
     197} // distribution
    186198
    187199//---------------------- Hyperbolic ----------------------
    188200
    189 static inline float sinh( float x ) { return sinhf( x ); }
    190 // extern "C" { double sinh( double ); }
    191 static inline long double sinh( long double x ) { return sinhl( x ); }
    192 static inline float _Complex sinh( float _Complex x ) { return csinhf( x ); }
    193 static inline double _Complex sinh( double _Complex x ) { return csinh( x ); }
    194 static inline long double _Complex sinh( long double _Complex x ) { return csinhl( x ); }
    195 
    196 static inline float cosh( float x ) { return coshf( x ); }
    197 // extern "C" { double cosh( double ); }
    198 static inline long double cosh( long double x ) { return coshl( x ); }
    199 static inline float _Complex cosh( float _Complex x ) { return ccoshf( x ); }
    200 static inline double _Complex cosh( double _Complex x ) { return ccosh( x ); }
    201 static inline long double _Complex cosh( long double _Complex x ) { return ccoshl( x ); }
    202 
    203 static inline float tanh( float x ) { return tanhf( x ); }
    204 // extern "C" { double tanh( double ); }
    205 static inline long double tanh( long double x ) { return tanhl( x ); }
    206 static inline float _Complex tanh( float _Complex x ) { return ctanhf( x ); }
    207 static inline double _Complex tanh( double _Complex x ) { return ctanh( x ); }
    208 static inline long double _Complex tanh( long double _Complex x ) { return ctanhl( x ); }
    209 
    210 static inline float asinh( float x ) { return asinhf( x ); }
    211 // extern "C" { double asinh( double ); }
    212 static inline long double asinh( long double x ) { return asinhl( x ); }
    213 static inline float _Complex asinh( float _Complex x ) { return casinhf( x ); }
    214 static inline double _Complex asinh( double _Complex x ) { return casinh( x ); }
    215 static inline long double _Complex asinh( long double _Complex x ) { return casinhl( x ); }
    216 
    217 static inline float acosh( float x ) { return acoshf( x ); }
    218 // extern "C" { double acosh( double ); }
    219 static inline long double acosh( long double x ) { return acoshl( x ); }
    220 static inline float _Complex acosh( float _Complex x ) { return cacoshf( x ); }
    221 static inline double _Complex acosh( double _Complex x ) { return cacosh( x ); }
    222 static inline long double _Complex acosh( long double _Complex x ) { return cacoshl( x ); }
    223 
    224 static inline float atanh( float x ) { return atanhf( x ); }
    225 // extern "C" { double atanh( double ); }
    226 static inline long double atanh( long double x ) { return atanhl( x ); }
    227 static inline float _Complex atanh( float _Complex x ) { return catanhf( x ); }
    228 static inline double _Complex atanh( double _Complex x ) { return catanh( x ); }
    229 static inline long double _Complex atanh( long double _Complex x ) { return catanhl( x ); }
     201static inline {
     202        float sinh( float x ) { return sinhf( x ); }
     203        // extern "C" { double sinh( double ); }
     204        long double sinh( long double x ) { return sinhl( x ); }
     205        float _Complex sinh( float _Complex x ) { return csinhf( x ); }
     206        double _Complex sinh( double _Complex x ) { return csinh( x ); }
     207        long double _Complex sinh( long double _Complex x ) { return csinhl( x ); }
     208
     209        float cosh( float x ) { return coshf( x ); }
     210        // extern "C" { double cosh( double ); }
     211        long double cosh( long double x ) { return coshl( x ); }
     212        float _Complex cosh( float _Complex x ) { return ccoshf( x ); }
     213        double _Complex cosh( double _Complex x ) { return ccosh( x ); }
     214        long double _Complex cosh( long double _Complex x ) { return ccoshl( x ); }
     215
     216        float tanh( float x ) { return tanhf( x ); }
     217        // extern "C" { double tanh( double ); }
     218        long double tanh( long double x ) { return tanhl( x ); }
     219        float _Complex tanh( float _Complex x ) { return ctanhf( x ); }
     220        double _Complex tanh( double _Complex x ) { return ctanh( x ); }
     221        long double _Complex tanh( long double _Complex x ) { return ctanhl( x ); }
     222
     223        float asinh( float x ) { return asinhf( x ); }
     224        // extern "C" { double asinh( double ); }
     225        long double asinh( long double x ) { return asinhl( x ); }
     226        float _Complex asinh( float _Complex x ) { return casinhf( x ); }
     227        double _Complex asinh( double _Complex x ) { return casinh( x ); }
     228        long double _Complex asinh( long double _Complex x ) { return casinhl( x ); }
     229
     230        float acosh( float x ) { return acoshf( x ); }
     231        // extern "C" { double acosh( double ); }
     232        long double acosh( long double x ) { return acoshl( x ); }
     233        float _Complex acosh( float _Complex x ) { return cacoshf( x ); }
     234        double _Complex acosh( double _Complex x ) { return cacosh( x ); }
     235        long double _Complex acosh( long double _Complex x ) { return cacoshl( x ); }
     236
     237        float atanh( float x ) { return atanhf( x ); }
     238        // extern "C" { double atanh( double ); }
     239        long double atanh( long double x ) { return atanhl( x ); }
     240        float _Complex atanh( float _Complex x ) { return catanhf( x ); }
     241        double _Complex atanh( double _Complex x ) { return catanh( x ); }
     242        long double _Complex atanh( long double _Complex x ) { return catanhl( x ); }
     243} // distribution
    230244
    231245//---------------------- Error / Gamma ----------------------
    232246
    233 static inline float erf( float x ) { return erff( x ); }
    234 // extern "C" { double erf( double ); }
    235 static inline long double erf( long double x ) { return erfl( x ); }
    236 // float _Complex erf( float _Complex );
    237 // double _Complex erf( double _Complex );
    238 // long double _Complex erf( long double _Complex );
    239 
    240 static inline float erfc( float x ) { return erfcf( x ); }
    241 // extern "C" { double erfc( double ); }
    242 static inline long double erfc( long double x ) { return erfcl( x ); }
    243 // float _Complex erfc( float _Complex );
    244 // double _Complex erfc( double _Complex );
    245 // long double _Complex erfc( long double _Complex );
    246 
    247 static inline float lgamma( float x ) { return lgammaf( x ); }
    248 // extern "C" { double lgamma( double ); }
    249 static inline long double lgamma( long double x ) { return lgammal( x ); }
    250 static inline float lgamma( float x, int * sign ) { return lgammaf_r( x, sign ); }
    251 static inline double lgamma( double x, int * sign ) { return lgamma_r( x, sign ); }
    252 static inline long double lgamma( long double x, int * sign ) { return lgammal_r( x, sign ); }
    253 
    254 static inline float tgamma( float x ) { return tgammaf( x ); }
    255 // extern "C" { double tgamma( double ); }
    256 static inline long double tgamma( long double x ) { return tgammal( x ); }
     247static inline {
     248        float erf( float x ) { return erff( x ); }
     249        // extern "C" { double erf( double ); }
     250        long double erf( long double x ) { return erfl( x ); }
     251        // float _Complex erf( float _Complex );
     252        // double _Complex erf( double _Complex );
     253        // long double _Complex erf( long double _Complex );
     254
     255        float erfc( float x ) { return erfcf( x ); }
     256        // extern "C" { double erfc( double ); }
     257        long double erfc( long double x ) { return erfcl( x ); }
     258        // float _Complex erfc( float _Complex );
     259        // double _Complex erfc( double _Complex );
     260        // long double _Complex erfc( long double _Complex );
     261
     262        float lgamma( float x ) { return lgammaf( x ); }
     263        // extern "C" { double lgamma( double ); }
     264        long double lgamma( long double x ) { return lgammal( x ); }
     265        float lgamma( float x, int * sign ) { return lgammaf_r( x, sign ); }
     266        double lgamma( double x, int * sign ) { return lgamma_r( x, sign ); }
     267        long double lgamma( long double x, int * sign ) { return lgammal_r( x, sign ); }
     268
     269        float tgamma( float x ) { return tgammaf( x ); }
     270        // extern "C" { double tgamma( double ); }
     271        long double tgamma( long double x ) { return tgammal( x ); }
     272} // distribution
    257273
    258274//---------------------- Nearest Integer ----------------------
    259275
    260 static inline float floor( float x ) { return floorf( x ); }
    261 // extern "C" { double floor( double ); }
    262 static inline long double floor( long double x ) { return floorl( x ); }
    263 
    264 static inline float ceil( float x ) { return ceilf( x ); }
    265 // extern "C" { double ceil( double ); }
    266 static inline long double ceil( long double x ) { return ceill( x ); }
    267 
    268 static inline float trunc( float x ) { return truncf( x ); }
    269 // extern "C" { double trunc( double ); }
    270 static inline long double trunc( long double x ) { return truncl( x ); }
    271 
    272 static inline float rint( float x ) { return rintf( x ); }
    273 // extern "C" { double rint( double x ); }
    274 static inline long double rint( long double x ) { return rintl( x ); }
    275 static inline long int rint( float x ) { return lrintf( x ); }
    276 static inline long int rint( double x ) { return lrint( x ); }
    277 static inline long int rint( long double x ) { return lrintl( x ); }
    278 static inline long long int rint( float x ) { return llrintf( x ); }
    279 static inline long long int rint( double x ) { return llrint( x ); }
    280 static inline long long int rint( long double x ) { return llrintl( x ); }
    281 
    282 static inline long int lrint( float x ) { return lrintf( x ); }
    283 // extern "C" { long int lrint( double ); }
    284 static inline long int lrint( long double x ) { return lrintl( x ); }
    285 static inline long long int llrint( float x ) { return llrintf( x ); }
    286 // extern "C" { long long int llrint( double ); }
    287 static inline long long int llrint( long double x ) { return llrintl( x ); }
    288 
    289 static inline float nearbyint( float x ) { return nearbyintf( x ); }
    290 // extern "C" { double nearbyint( double ); }
    291 static inline long double nearbyint( long double x ) { return nearbyintl( x ); }
    292 
    293 static inline float round( float x ) { return roundf( x ); }
    294 // extern "C" { double round( double x ); }
    295 static inline long double round( long double x ) { return roundl( x ); }
    296 static inline long int round( float x ) { return lroundf( x ); }
    297 static inline long int round( double x ) { return lround( x ); }
    298 static inline long int round( long double x ) { return lroundl( x ); }
    299 static inline long long int round( float x ) { return llroundf( x ); }
    300 static inline long long int round( double x ) { return llround( x ); }
    301 static inline long long int round( long double x ) { return llroundl( x ); }
    302 
    303 static inline long int lround( float x ) { return lroundf( x ); }
    304 // extern "C" { long int lround( double ); }
    305 static inline long int lround( long double x ) { return lroundl( x ); }
    306 static inline long long int llround( float x ) { return llroundf( x ); }
    307 // extern "C" { long long int llround( double ); }
    308 static inline long long int llround( long double x ) { return llroundl( x ); }
     276static inline {
     277        signed char floor( signed char n, signed char align ) { return n / align * align; }
     278        unsigned char floor( unsigned char n, unsigned char align ) { return n / align * align; }
     279        short int floor( short int n, short int align ) { return n / align * align; }
     280        unsigned short int floor( unsigned short int n, unsigned short int align ) { return n / align * align; }
     281        int floor( int n, int align ) { return n / align * align; }
     282        unsigned int floor( unsigned int n, unsigned int align ) { return n / align * align; }
     283        long int floor( long int n, long int align ) { return n / align * align; }
     284        unsigned long int floor( unsigned long int n, unsigned long int align ) { return n / align * align; }
     285        long long int floor( long long int n, long long int align ) { return n / align * align; }
     286        unsigned long long int floor( unsigned long long int n, unsigned long long int align ) { return n / align * align; }
     287
     288        // forall( otype T | { T ?/?( T, T ); T ?*?( T, T ); } )
     289        // T floor( T n, T align ) { return n / align * align; }
     290
     291        signed char ceiling_div( signed char n, char align ) { return (n + (align - 1)) / align; }
     292        unsigned char ceiling_div( unsigned char n, unsigned char align ) { return (n + (align - 1)) / align; }
     293        short int ceiling_div( short int n, short int align ) { return (n + (align - 1)) / align; }
     294        unsigned short int ceiling_div( unsigned short int n, unsigned short int align ) { return (n + (align - 1)) / align; }
     295        int ceiling_div( int n, int align ) { return (n + (align - 1)) / align; }
     296        unsigned int ceiling_div( unsigned int n, unsigned int align ) { return (n + (align - 1)) / align; }
     297        long int ceiling_div( long int n, long int align ) { return (n + (align - 1)) / align; }
     298        unsigned long int ceiling_div( unsigned long int n, unsigned long int align ) { return (n + (align - 1)) / align; }
     299        long long int ceiling_div( long long int n, long long int align ) { return (n + (align - 1)) / align; }
     300        unsigned long long int ceiling_div( unsigned long long int n, unsigned long long int align ) { return (n + (align - 1)) / align; }
     301
     302        // forall( otype T | { T ?+?( T, T ); T ?-?( T, T ); T ?%?( T, T ); } )
     303        // T ceiling_div( T n, T align ) { verify( is_pow2( align ) );return (n + (align - 1)) / align; }
     304       
     305        // gcc notices the div/mod pair and saves both so only one div.
     306        signed char ceiling( signed char n, signed char align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     307        unsigned char ceiling( unsigned char n, unsigned char align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     308        short int ceiling( short int n, short int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     309        unsigned short int ceiling( unsigned short int n, unsigned short int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     310        int ceiling( int n, int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     311        unsigned int ceiling( unsigned int n, unsigned int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     312        long int ceiling( long int n, long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     313        unsigned long int ceiling( unsigned long int n, unsigned long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0) , align); }
     314        long long int ceiling( long long int n, long long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     315        unsigned long long int ceiling( unsigned long long int n, unsigned long long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }
     316
     317        // forall( otype T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T ); T ?/?( T, T ); } )
     318        // T ceiling( T n, T align ) { return return floor( n + (n % align != 0 ? align - 1 : 0), align ); *}
     319
     320        float floor( float x ) { return floorf( x ); }
     321        // extern "C" { double floor( double ); }
     322        long double floor( long double x ) { return floorl( x ); }
     323
     324        float ceil( float x ) { return ceilf( x ); }
     325        // extern "C" { double ceil( double ); }
     326        long double ceil( long double x ) { return ceill( x ); }
     327
     328        float trunc( float x ) { return truncf( x ); }
     329        // extern "C" { double trunc( double ); }
     330        long double trunc( long double x ) { return truncl( x ); }
     331
     332        float rint( float x ) { return rintf( x ); }
     333        // extern "C" { double rint( double x ); }
     334        long double rint( long double x ) { return rintl( x ); }
     335        long int rint( float x ) { return lrintf( x ); }
     336        long int rint( double x ) { return lrint( x ); }
     337        long int rint( long double x ) { return lrintl( x ); }
     338        long long int rint( float x ) { return llrintf( x ); }
     339        long long int rint( double x ) { return llrint( x ); }
     340        long long int rint( long double x ) { return llrintl( x ); }
     341
     342        long int lrint( float x ) { return lrintf( x ); }
     343        // extern "C" { long int lrint( double ); }
     344        long int lrint( long double x ) { return lrintl( x ); }
     345        long long int llrint( float x ) { return llrintf( x ); }
     346        // extern "C" { long long int llrint( double ); }
     347        long long int llrint( long double x ) { return llrintl( x ); }
     348
     349        float nearbyint( float x ) { return nearbyintf( x ); }
     350        // extern "C" { double nearbyint( double ); }
     351        long double nearbyint( long double x ) { return nearbyintl( x ); }
     352
     353        float round( float x ) { return roundf( x ); }
     354        // extern "C" { double round( double x ); }
     355        long double round( long double x ) { return roundl( x ); }
     356        long int round( float x ) { return lroundf( x ); }
     357        long int round( double x ) { return lround( x ); }
     358        long int round( long double x ) { return lroundl( x ); }
     359        long long int round( float x ) { return llroundf( x ); }
     360        long long int round( double x ) { return llround( x ); }
     361        long long int round( long double x ) { return llroundl( x ); }
     362
     363        long int lround( float x ) { return lroundf( x ); }
     364        // extern "C" { long int lround( double ); }
     365        long int lround( long double x ) { return lroundl( x ); }
     366        long long int llround( float x ) { return llroundf( x ); }
     367        // extern "C" { long long int llround( double ); }
     368        long long int llround( long double x ) { return llroundl( x ); }
     369} // distribution
    309370
    310371//---------------------- Manipulation ----------------------
    311372
    312 static inline float copysign( float x, float y ) { return copysignf( x, y ); }
    313 // extern "C" { double copysign( double, double ); }
    314 static inline long double copysign( long double x, long double y ) { return copysignl( x, y ); }
    315 
    316 static inline float frexp( float x, int * ip ) { return frexpf( x, ip ); }
    317 // extern "C" { double frexp( double, int * ); }
    318 static inline long double frexp( long double x, int * ip ) { return frexpl( x, ip ); }
    319 
    320 static inline float ldexp( float x, int exp2 ) { return ldexpf( x, exp2 ); }
    321 // extern "C" { double ldexp( double, int ); }
    322 static inline long double ldexp( long double x, int exp2 ) { return ldexpl( x, exp2 ); }
    323 
    324 static inline [ float, float ] modf( float x ) { float i; x = modff( x, &i ); return [ i, x ]; }
    325 static inline float modf( float x, float * i ) { return modff( x, i ); }
    326 static inline [ double, double ] modf( double x ) { double i; x = modf( x, &i ); return [ i, x ]; }
    327 // extern "C" { double modf( double, double * ); }
    328 static inline [ long double, long double ] modf( long double x ) { long double i; x = modfl( x, &i ); return [ i, x ]; }
    329 static inline long double modf( long double x, long double * i ) { return modfl( x, i ); }
    330 
    331 static inline float nextafter( float x, float y ) { return nextafterf( x, y ); }
    332 // extern "C" { double nextafter( double, double ); }
    333 static inline long double nextafter( long double x, long double y ) { return nextafterl( x, y ); }
    334 
    335 static inline float nexttoward( float x, long double y ) { return nexttowardf( x, y ); }
    336 // extern "C" { double nexttoward( double, long double ); }
    337 static inline long double nexttoward( long double x, long double y ) { return nexttowardl( x, y ); }
    338 
    339 static inline float scalbn( float x, int exp ) { return scalbnf( x, exp ); }
    340 // extern "C" { double scalbn( double, int ); }
    341 static inline long double scalbn( long double x, int exp ) { return scalbnl( x, exp ); }
    342 static inline float scalbn( float x, long int exp ) { return scalblnf( x, exp ); }
    343 static inline double scalbn( double x, long int exp ) { return scalbln( x, exp ); }
    344 static inline long double scalbn( long double x, long int exp ) { return scalblnl( x, exp ); }
    345 
    346 static inline float scalbln( float x, long int exp ) { return scalblnf( x, exp ); }
    347 // extern "C" { double scalbln( double, long int ); }
    348 static inline long double scalbln( long double x, long int exp ) { return scalblnl( x, exp ); }
     373static inline {
     374        float copysign( float x, float y ) { return copysignf( x, y ); }
     375        // extern "C" { double copysign( double, double ); }
     376        long double copysign( long double x, long double y ) { return copysignl( x, y ); }
     377
     378        float frexp( float x, int * ip ) { return frexpf( x, ip ); }
     379        // extern "C" { double frexp( double, int * ); }
     380        long double frexp( long double x, int * ip ) { return frexpl( x, ip ); }
     381
     382        float ldexp( float x, int exp2 ) { return ldexpf( x, exp2 ); }
     383        // extern "C" { double ldexp( double, int ); }
     384        long double ldexp( long double x, int exp2 ) { return ldexpl( x, exp2 ); }
     385
     386        [ float, float ] modf( float x ) { float i; x = modff( x, &i ); return [ i, x ]; }
     387        float modf( float x, float * i ) { return modff( x, i ); }
     388        [ double, double ] modf( double x ) { double i; x = modf( x, &i ); return [ i, x ]; }
     389        // extern "C" { double modf( double, double * ); }
     390        [ long double, long double ] modf( long double x ) { long double i; x = modfl( x, &i ); return [ i, x ]; }
     391        long double modf( long double x, long double * i ) { return modfl( x, i ); }
     392
     393        float nextafter( float x, float y ) { return nextafterf( x, y ); }
     394        // extern "C" { double nextafter( double, double ); }
     395        long double nextafter( long double x, long double y ) { return nextafterl( x, y ); }
     396
     397        float nexttoward( float x, long double y ) { return nexttowardf( x, y ); }
     398        // extern "C" { double nexttoward( double, long double ); }
     399        long double nexttoward( long double x, long double y ) { return nexttowardl( x, y ); }
     400
     401        float scalbn( float x, int exp ) { return scalbnf( x, exp ); }
     402        // extern "C" { double scalbn( double, int ); }
     403        long double scalbn( long double x, int exp ) { return scalbnl( x, exp ); }
     404        float scalbn( float x, long int exp ) { return scalblnf( x, exp ); }
     405        double scalbn( double x, long int exp ) { return scalbln( x, exp ); }
     406        long double scalbn( long double x, long int exp ) { return scalblnl( x, exp ); }
     407
     408        float scalbln( float x, long int exp ) { return scalblnf( x, exp ); }
     409        // extern "C" { double scalbln( double, long int ); }
     410        long double scalbln( long double x, long int exp ) { return scalblnl( x, exp ); }
     411} // distribution
    349412
    350413//---------------------------------------
    351414
    352 #include "common.hfa"
    353 
    354 //---------------------------------------
    355 
    356 forall( otype T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } )
    357 T lerp( T x, T y, T a ) { return x * ((T){1} - a) + y * a; }
    358 
    359 forall( otype T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } )
    360 T step( T edge, T x ) { return x < edge ? (T){0} : (T){1}; }
    361 
    362 forall( otype T | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } )
    363 T smoothstep( T edge0, T edge1, T x ) { T t = clamp( (x - edge0) / (edge1 - edge0), (T){0}, (T){1} ); return t * t * ((T){3} - (T){2} * t); }
     415static inline {
     416        forall( otype T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } )
     417        T lerp( T x, T y, T a ) { return x * ((T){1} - a) + y * a; }
     418
     419        forall( otype T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } )
     420        T step( T edge, T x ) { return x < edge ? (T){0} : (T){1}; }
     421
     422        forall( otype T | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } )
     423        T smoothstep( T edge0, T edge1, T x ) { T t = clamp( (x - edge0) / (edge1 - edge0), (T){0}, (T){1} ); return t * t * ((T){3} - (T){2} * t); }
     424} // distribution
    364425
    365426// Local Variables: //
  • libcfa/src/startup.cfa

    r3c64c668 r58fe85a  
    1414//
    1515
    16 #include <time.h>                                                                               // tzset
     16#include <time.h>                // tzset
     17#include <locale.h>        // setlocale
    1718#include "startup.hfa"
    1819
     
    2122    void __cfaabi_appready_startup( void ) {
    2223                tzset();                                                                                // initialize time global variables
     24                setlocale(LC_NUMERIC, "");
    2325                #ifdef __CFA_DEBUG__
    2426                extern void heapAppStart();
     
    4143struct __spinlock_t;
    4244extern "C" {
    43         void __cfaabi_dbg_record(struct __spinlock_t & this, const char prev_name[]) __attribute__(( weak )) {}
     45        void __cfaabi_dbg_record_lock(struct __spinlock_t & this, const char prev_name[]) __attribute__(( weak )) {}
    4446}
    4547
  • libcfa/src/stdhdr/assert.h

    r3c64c668 r58fe85a  
    3333        #define verify(x) assert(x)
    3434        #define verifyf(x, ...) assertf(x, __VA_ARGS__)
     35        #define verifyfail(...)
    3536        #define __CFA_WITH_VERIFY__
    3637#else
    3738        #define verify(x)
    3839        #define verifyf(x, ...)
     40        #define verifyfail(...)
    3941#endif
    4042
  • libcfa/src/stdhdr/malloc.h

    r3c64c668 r58fe85a  
    1010// Created On       : Thu Jul 20 15:58:16 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Aug 11 09:06:31 2018
    13 // Update Count     : 10
     12// Last Modified On : Wed May 27 14:13:14 2020
     13// Update Count     : 18
    1414//
    15 
    16 
    17 size_t default_mmap_start();                                                    // CFA extras
    18 size_t default_heap_expansion();
    19 
    20 bool traceHeap();
    21 bool traceHeapOn();
    22 bool traceHeapOff();
    23 
    24 bool traceHeapTerm();
    25 bool traceHeapTermOn();
    26 bool traceHeapTermOff();
    27 
    28 bool checkFree();
    29 bool checkFreeOn();
    30 bool checkFreeOff();
    31 
    32 extern "C" {
    33 size_t malloc_alignment( void * );
    34 bool malloc_zero_fill( void * );
    35 int malloc_stats_fd( int fd );
    36 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize );
    37 } // extern "C"
    3815
    3916extern "C" {
    4017#include_next <malloc.h>                                                                // has internal check for multiple expansion
    4118} // extern "C"
     19
     20#include <heap.hfa>
    4221
    4322// Local Variables: //
  • libcfa/src/stdlib.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Thu Jan 28 17:10:29 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 08:27:08 2020
    13 // Update Count     : 486
     12// Last Modified On : Thu Nov 12 07:46:09 2020
     13// Update Count     : 503
    1414//
    1515
     
    2020#define _XOPEN_SOURCE 600                                                               // posix_memalign, *rand48
    2121#include <string.h>                                                                             // memcpy, memset
    22 #include <malloc.h>                                                                             // malloc_usable_size
    2322//#include <math.h>                                                                             // fabsf, fabs, fabsl
    2423#include <complex.h>                                                                    // _Complex_I
     
    2726//---------------------------------------
    2827
    29 forall( dtype T | sized(T) ) {
    30         T * alloc_set( T ptr[], size_t dim, char fill ) {       // realloc array with fill
    31                 size_t olen = malloc_usable_size( ptr );                // current allocation
    32                 void * nptr = (void *)realloc( (void *)ptr, dim * sizeof(T) ); // C realloc
    33                 size_t nlen = malloc_usable_size( nptr );               // new allocation
    34                 if ( nlen > olen ) {                                                    // larger ?
    35                         memset( (char *)nptr + olen, (int)fill, nlen - olen ); // initialize added storage
    36                 } // if
    37                 return (T *)nptr;
    38         } // alloc_set
    39 
    40         T * alloc_align_set( T ptr[], size_t align, char fill ) { // aligned realloc with fill
    41                 size_t olen = malloc_usable_size( ptr );                // current allocation
    42                 void * nptr = (void *)realloc( (void *)ptr, align, sizeof(T) ); // CFA realloc
    43                 // char * nptr = alloc_align( ptr, align );
    44                 size_t nlen = malloc_usable_size( nptr );               // new allocation
    45                 if ( nlen > olen ) {                                                    // larger ?
    46                         memset( (char *)nptr + olen, (int)fill, nlen - olen ); // initialize added storage
    47                 } // if
    48                 return (T *)nptr;
    49         } // alloc_align_set
    50 } // distribution
    51 
    52 // allocation/deallocation and constructor/destructor, non-array types
    53 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } )
    54 T * new( Params p ) {
    55         return &(*malloc()){ p };                                                       // run constructor
    56 } // new
    57 
    58 forall( dtype T | sized(T) | { void ^?{}( T & ); } )
    59 void delete( T * ptr ) {
    60         if ( ptr ) {                                                                            // ignore null
    61                 ^(*ptr){};                                                                              // run destructor
    62                 free( ptr );
    63         } // if
    64 } // delete
    65 
    66 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } )
    67 void delete( T * ptr, Params rest ) {
    68         if ( ptr ) {                                                                            // ignore null
    69                 ^(*ptr){};                                                                              // run destructor
    70                 free( ptr );
    71         } // if
    72         delete( rest );
    73 } // delete
    74 
    75 
    76 // allocation/deallocation and constructor/destructor, array types
    77 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } )
    78 T * anew( size_t dim, Params p ) {
     28// Cforall allocation/deallocation and constructor/destructor, array types
     29
     30forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } )
     31T * anew( size_t dim, TT p ) {
    7932        T * arr = alloc( dim );
    8033        for ( unsigned int i = 0; i < dim; i += 1 ) {
     
    8538
    8639forall( dtype T | sized(T) | { void ^?{}( T & ); } )
    87 void adelete( size_t dim, T arr[] ) {
     40void adelete( T arr[] ) {
    8841        if ( arr ) {                                                                            // ignore null
     42                size_t dim = malloc_size( arr ) / sizeof( T );
    8943                for ( int i = dim - 1; i >= 0; i -= 1 ) {               // reverse allocation order, must be unsigned
    9044                        ^(arr[i]){};                                                            // run destructor
     
    9448} // adelete
    9549
    96 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params ); } )
    97 void adelete( size_t dim, T arr[], Params rest ) {
     50forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT | { void adelete( TT ); } )
     51void adelete( T arr[], TT rest ) {
    9852        if ( arr ) {                                                                            // ignore null
     53                size_t dim = malloc_size( arr ) / sizeof( T );
    9954                for ( int i = dim - 1; i >= 0; i -= 1 ) {               // reverse allocation order, must be unsigned
    10055                        ^(arr[i]){};                                                            // run destructor
     
    255210extern "C" {                                                                                    // override C version
    256211        void srandom( unsigned int seed ) { srand48( (long int)seed ); }
    257         long int random( void ) { return mrand48(); }
     212        long int random( void ) { return mrand48(); }           // GENERATES POSITIVE AND NEGATIVE VALUES
    258213} // extern "C"
    259214
  • libcfa/src/stdlib.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Thu Jan 28 17:12:35 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 08:27:01 2020
    13 // Update Count     : 401
     12// Last Modified On : Sat Dec 12 13:52:34 2020
     13// Update Count     : 536
    1414//
    1515
    1616#pragma once
    1717
    18 #include "bits/defs.hfa"
    19 #include "bits/align.hfa"
     18#include "bits/defs.hfa"                                                                // OPTIONAL_THREAD
     19#include "bits/align.hfa"                                                               // libAlign
    2020
    2121#include <stdlib.h>                                                                             // *alloc, strto*, ato*
    22 
     22#include <heap.hfa>
     23
     24// Reduce includes by explicitly defining these routines.
    2325extern "C" {
    24         void * memalign( size_t align, size_t size );           // malloc.h
     26        void * memalign( size_t alignment, size_t size );       // malloc.h
     27        void * pvalloc( size_t size );                                          // malloc.h
    2528        void * memset( void * dest, int fill, size_t size ); // string.h
    2629        void * memcpy( void * dest, const void * src, size_t size ); // string.h
    27     void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize ); // CFA heap
    2830} // extern "C"
    29 
    30 void * realloc( void * oaddr, size_t nalign, size_t size ); // CFA heap
    3131
    3232//---------------------------------------
     
    3939//---------------------------------------
    4040
     41#include "common.hfa"
     42
     43//---------------------------------------
     44
     45// Macro because of returns
     46#define $ARRAY_ALLOC( allocation, alignment, dim ) \
     47        if ( _Alignof(T) <= libAlign() ) return (T *)(void *)allocation( dim, (size_t)sizeof(T) ); /* C allocation */ \
     48        else return (T *)alignment( _Alignof(T), dim, sizeof(T) )
     49
    4150static inline forall( dtype T | sized(T) ) {
    42         // C dynamic allocation
     51        // CFA safe equivalents, i.e., implicit size specification
    4352
    4453        T * malloc( void ) {
    45                 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( (size_t)sizeof(T) ); // C malloc
     54                if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( (size_t)sizeof(T) ); // C allocation
    4655                else return (T *)memalign( _Alignof(T), sizeof(T) );
    4756        } // malloc
    4857
     58        T * aalloc( size_t dim ) {
     59                $ARRAY_ALLOC( aalloc, amemalign, dim );
     60        } // aalloc
     61
    4962        T * calloc( size_t dim ) {
    50                 if ( _Alignof(T) <= libAlign() )return (T *)(void *)calloc( dim, sizeof(T) ); // C calloc
    51                 else return (T *)cmemalign( _Alignof(T), dim, sizeof(T) );
     63                $ARRAY_ALLOC( calloc, cmemalign, dim );
    5264        } // calloc
    5365
     66        T * resize( T * ptr, size_t size ) {                            // CFA resize, eliminate return-type cast
     67                if ( _Alignof(T) <= libAlign() ) return (T *)(void *)resize( (void *)ptr, size ); // CFA resize
     68                else return (T *)(void *)resize( (void *)ptr, _Alignof(T), size ); // CFA resize
     69        } // resize
     70
    5471        T * realloc( T * ptr, size_t size ) {                           // CFA realloc, eliminate return-type cast
    55                 return (T *)(void *)realloc( (void *)ptr, size ); // C realloc
     72                if ( _Alignof(T) <= libAlign() ) return (T *)(void *)realloc( (void *)ptr, size ); // C realloc
     73                else return (T *)(void *)realloc( (void *)ptr, _Alignof(T), size ); // CFA realloc
    5674        } // realloc
    5775
     
    6078        } // memalign
    6179
     80        T * amemalign( size_t align, size_t dim ) {
     81                return (T *)amemalign( align, dim, sizeof(T) ); // CFA amemalign
     82        } // amemalign
     83
    6284        T * cmemalign( size_t align, size_t dim  ) {
    6385                return (T *)cmemalign( align, dim, sizeof(T) ); // CFA cmemalign
     
    7294        } // posix_memalign
    7395
    74         // Cforall dynamic allocation
    75 
    76         T * alloc( void ) {
    77                 return malloc();
    78         } // alloc
    79 
    80         T * alloc( size_t dim ) {
    81                 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( dim * (size_t)sizeof(T) );
    82                 else return (T *)memalign( _Alignof(T), dim * sizeof(T) );
    83         } // alloc
    84 
    85         T * alloc( T ptr[], size_t dim ) {                                      // realloc
    86                 return (T *)(void *)realloc( (void *)ptr, dim * sizeof(T) ); // C realloc
    87         } // alloc
    88 
    89         T * alloc_set( char fill ) {
    90                 return (T *)memset( (T *)alloc(), (int)fill, sizeof(T) ); // initialize with fill value
    91         } // alloc
    92 
    93         T * alloc_set( T fill ) {
    94                 return (T *)memcpy( (T *)alloc(), &fill, sizeof(T) ); // initialize with fill value
    95         } // alloc
    96 
    97         T * alloc_set( size_t dim, char fill ) {
    98                 return (T *)memset( (T *)alloc( dim ), (int)fill, dim * sizeof(T) ); // initialize with fill value
    99         } // alloc
    100 
    101         T * alloc_set( size_t dim, T fill ) {
    102                 T * r = (T *)alloc( dim );
    103                 for ( i; dim ) { memcpy( &r[i], &fill, sizeof(T) ); } // initialize with fill value
    104                 return r;
    105         } // alloc
    106 
    107         T * alloc_set( size_t dim, const T fill[] ) {
    108                 return (T *)memcpy( (T *)alloc( dim ), fill, dim * sizeof(T) ); // initialize with fill value
    109         } // alloc
    110 } // distribution
    111 
    112 forall( dtype T | sized(T) ) {
    113         T * alloc_set( T ptr[], size_t dim, char fill );        // realloc array with fill
    114 } // distribution
     96        T * valloc( void ) {
     97                return (T *)valloc( sizeof(T) );                                // C valloc
     98        } // valloc
     99
     100        T * pvalloc( void ) {
     101                return (T *)pvalloc( sizeof(T) );                               // C pvalloc
     102        } // pvalloc
     103} // distribution
     104
     105/*
     106        FIX ME : fix alloc interface after Ticker Number 214 is resolved, define and add union to S_fill. Then, modify postfix-fill functions to support T * with nmemb, char, and T object of any size. Finally, change alloc_internal.
     107        Or, just follow the instructions below for that.
     108
     109        1. Replace the current forall-block that contains defintions of S_fill and S_realloc with following:
     110                forall( dtype T | sized(T) ) {
     111                        union  U_fill           { char c; T * a; T t; };
     112                        struct S_fill           { char tag; U_fill(T) fill; };
     113                        struct S_realloc        { inline T *; };
     114                }
     115
     116        2. Replace all current postfix-fill functions with following for updated S_fill:
     117                S_fill(T) ?`fill( char a )                                      { S_fill(T) ret = {'c'}; ret.fill.c = a; return ret; }
     118                S_fill(T) ?`fill( T    a )                                      { S_fill(T) ret = {'t'}; memcpy(&ret.fill.t, &a, sizeof(T)); return ret; }
     119                S_fill(T) ?`fill( T    a[], size_t nmemb )      { S_fill(T) ret = {'a', nmemb}; ret.fill.a = a; return ret; }
     120
     121        3. Replace the $alloc_internal function which is outside ttype forall-block with following function:
     122                T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill) {
     123                        T * ptr = NULL;
     124                        size_t size = sizeof(T);
     125                        size_t copy_end = 0;
     126
     127                        if(Resize) {
     128                                ptr = (T*) (void *) resize( (int *)Resize, Align, Dim * size );
     129                        } else if (Realloc) {
     130                                if (Fill.tag != '0') copy_end = min(malloc_size( Realloc ), Dim * size);
     131                                ptr = (T*) (void *) realloc( (int *)Realloc, Align, Dim * size );
     132                        } else {
     133                                ptr = (T*) (void *) memalign( Align, Dim * size );
     134                        }
     135
     136                        if(Fill.tag == 'c') {
     137                                memset( (char *)ptr + copy_end, (int)Fill.fill.c, Dim * size - copy_end );
     138                        } else if(Fill.tag == 't') {
     139                                for ( int i = copy_end; i <= Dim * size - size ; i += size ) {
     140                                        memcpy( (char *)ptr + i, &Fill.fill.t, size );
     141                                }
     142                        } else if(Fill.tag == 'a') {
     143                                memcpy( (char *)ptr + copy_end, Fill.fill.a, min(Dim * size - copy_end, size * Fill.nmemb) );
     144                        }
     145
     146                        return ptr;
     147                } // $alloc_internal
     148*/
     149
     150typedef struct S_align                  { inline size_t;  } T_align;
     151typedef struct S_resize                 { inline void *;  }     T_resize;
     152
     153forall( dtype T ) {
     154        struct S_fill           { char tag; char c; size_t size; T * at; char t[50]; };
     155        struct S_realloc        { inline T *; };
     156}
     157
     158static inline T_align   ?`align   ( size_t a )  { return (T_align){a}; }
     159static inline T_resize  ?`resize  ( void * a )  { return (T_resize){a}; }
    115160
    116161static inline forall( dtype T | sized(T) ) {
    117         T * alloc_align( size_t align ) {
    118                 return (T *)memalign( align, sizeof(T) );
    119         } // alloc_align
    120 
    121         T * alloc_align( size_t align, size_t dim ) {
    122                 return (T *)memalign( align, dim * sizeof(T) );
    123         } // alloc_align
    124 
    125         T * alloc_align( T ptr[], size_t align ) {                      // aligned realloc array
    126                 return (T *)(void *)realloc( (void *)ptr, align, sizeof(T) ); // CFA realloc
    127         } // alloc_align
    128 
    129         T * alloc_align( T ptr[], size_t align, size_t dim ) { // aligned realloc array
    130                 return (T *)(void *)realloc( (void *)ptr, align, dim * sizeof(T) ); // CFA realloc
    131         } // alloc_align
    132 
    133         T * alloc_align_set( size_t align, char fill ) {
    134                 return (T *)memset( (T *)alloc_align( align ), (int)fill, sizeof(T) ); // initialize with fill value
    135         } // alloc_align
    136 
    137         T * alloc_align_set( size_t align, T fill ) {
    138                 return (T *)memcpy( (T *)alloc_align( align ), &fill, sizeof(T) ); // initialize with fill value
    139         } // alloc_align
    140 
    141         T * alloc_align_set( size_t align, size_t dim, char fill ) {
    142                 return (T *)memset( (T *)alloc_align( align, dim ), (int)fill, dim * sizeof(T) ); // initialize with fill value
    143         } // alloc_align
    144 
    145         T * alloc_align_set( size_t align, size_t dim, T fill ) {
    146                 T * r = (T *)alloc_align( align, dim );
    147                 for ( i; dim ) { memcpy( &r[i], &fill, sizeof(T) ); } // initialize with fill value
    148                 return r;
    149         } // alloc_align
    150 
    151         T * alloc_align_set( size_t align, size_t dim, const T fill[] ) {
    152                 return (T *)memcpy( (T *)alloc_align( align, dim ), fill, dim * sizeof(T) );
    153         } // alloc_align
    154 } // distribution
    155 
    156 forall( dtype T | sized(T) ) {
    157         T * alloc_align_set( T ptr[], size_t align, size_t dim, char fill ); // aligned realloc array with fill
    158 } // distribution
     162        S_fill(T) ?`fill ( T t ) {
     163                S_fill(T) ret = { 't' };
     164                size_t size = sizeof(T);
     165                if(size > sizeof(ret.t)) { printf("ERROR: const object of size greater than 50 bytes given for dynamic memory fill\n"); exit(1); }
     166                memcpy( &ret.t, &t, size );
     167                return ret;
     168        }
     169        S_fill(T)               ?`fill ( char c )                               { return (S_fill(T)){ 'c', c }; }
     170        S_fill(T)               ?`fill ( T * a )                                { return (S_fill(T)){ 'T', '0', 0, a }; }
     171        S_fill(T)               ?`fill ( T a[], size_t nmemb )  { return (S_fill(T)){ 'a', '0', nmemb * sizeof(T), a }; }
     172
     173        S_realloc(T)    ?`realloc ( T * a )                             { return (S_realloc(T)){a}; }
     174
     175        T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill) {
     176                T * ptr = NULL;
     177                size_t size = sizeof(T);
     178                size_t copy_end = 0;
     179
     180                if ( Resize ) {
     181                        ptr = (T*) (void *) resize( (void *)Resize, Align, Dim * size );
     182                } else if ( Realloc ) {
     183                        if (Fill.tag != '0') copy_end = min(malloc_size( Realloc ), Dim * size);
     184                        ptr = (T*) (void *) realloc( (void *)Realloc, Align, Dim * size );
     185                } else {
     186                        ptr = (T*) (void *) memalign( Align, Dim * size );
     187                }
     188
     189                if(Fill.tag == 'c') {
     190                        memset( (char *)ptr + copy_end, (int)Fill.c, Dim * size - copy_end );
     191                } else if(Fill.tag == 't') {
     192                        for ( int i = copy_end; i < Dim * size; i += size ) {
     193                                memcpy( (char *)ptr + i, &Fill.t, size );
     194                        }
     195                } else if(Fill.tag == 'a') {
     196                        memcpy( (char *)ptr + copy_end, Fill.at, min(Dim * size - copy_end, Fill.size) );
     197                } else if(Fill.tag == 'T') {
     198                        for ( int i = copy_end; i < Dim * size; i += size ) {
     199                                memcpy( (char *)ptr + i, Fill.at, size );
     200                        }
     201                }
     202
     203                return ptr;
     204        } // $alloc_internal
     205
     206        forall( ttype TT | { T * $alloc_internal( void *, T *, size_t, size_t, S_fill(T), TT ); } ) {
     207
     208                T * $alloc_internal( void *       , T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill, T_resize Resize, TT rest) {
     209                return $alloc_internal( Resize, (T*)0p, Align, Dim, Fill, rest);
     210                }
     211
     212                T * $alloc_internal( void * Resize, T *        , size_t Align, size_t Dim, S_fill(T) Fill, S_realloc(T) Realloc, TT rest) {
     213                return $alloc_internal( (void*)0p, Realloc, Align, Dim, Fill, rest);
     214                }
     215
     216                T * $alloc_internal( void * Resize, T * Realloc, size_t      , size_t Dim, S_fill(T) Fill, T_align Align, TT rest) {
     217                return $alloc_internal( Resize, Realloc, Align, Dim, Fill, rest);
     218                }
     219
     220                T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T)     , S_fill(T) Fill, TT rest) {
     221                return $alloc_internal( Resize, Realloc, Align, Dim, Fill, rest);
     222                }
     223
     224            T * alloc( TT all ) {
     225                return $alloc_internal( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), (size_t)1, (S_fill(T)){'0'}, all);
     226            }
     227
     228            T * alloc( size_t dim, TT all ) {
     229                return $alloc_internal( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), dim, (S_fill(T)){'0'}, all);
     230            }
     231
     232        } // distribution TT
     233} // distribution T
    159234
    160235static inline forall( dtype T | sized(T) ) {
    161         // data, non-array types
     236        // CFA safe initialization/copy, i.e., implicit size specification, non-array types
    162237        T * memset( T * dest, char fill ) {
    163238                return (T *)memset( dest, fill, sizeof(T) );
     
    167242                return (T *)memcpy( dest, src, sizeof(T) );
    168243        } // memcpy
    169 } // distribution
    170 
    171 static inline forall( dtype T | sized(T) ) {
    172         // data, array types
     244
     245        // CFA safe initialization/copy, i.e., implicit size specification, array types
    173246        T * amemset( T dest[], char fill, size_t dim ) {
    174247                return (T *)(void *)memset( dest, fill, dim * sizeof(T) ); // C memset
     
    180253} // distribution
    181254
    182 // allocation/deallocation and constructor/destructor, non-array types
    183 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p );
    184 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr );
    185 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } ) void delete( T * ptr, Params rest );
    186 
    187 // allocation/deallocation and constructor/destructor, array types
    188 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size_t dim, Params p );
    189 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( size_t dim, T arr[] );
    190 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params ); } ) void adelete( size_t dim, T arr[], Params rest );
     255// CFA deallocation for multiple objects
     256static inline forall( dtype T )                                                 // FIX ME, problems with 0p in list
     257void free( T * ptr ) {
     258        free( (void *)ptr );                                                            // C free
     259} // free
     260static inline forall( dtype T, ttype TT | { void free( TT ); } )
     261void free( T * ptr, TT rest ) {
     262        free( ptr );
     263        free( rest );
     264} // free
     265
     266// CFA allocation/deallocation and constructor/destructor, non-array types
     267static inline forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } )
     268T * new( TT p ) {
     269        return &(*(T *)malloc()){ p };                                                  // run constructor
     270} // new
     271
     272static inline forall( dtype T | { void ^?{}( T & ); } )
     273void delete( T * ptr ) {
     274        // special case for 0-sized object => always call destructor
     275        if ( ptr || sizeof(ptr) == 0 ) {                                        // ignore null but not 0-sized objects
     276                ^(*ptr){};                                                                              // run destructor
     277        } // if
     278        free( ptr );                                                                            // always call free
     279} // delete
     280static inline forall( dtype T, ttype TT | { void ^?{}( T & ); void delete( TT ); } )
     281void delete( T * ptr, TT rest ) {
     282        delete( ptr );
     283        delete( rest );
     284} // delete
     285
     286// CFA allocation/deallocation and constructor/destructor, array types
     287forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } ) T * anew( size_t dim, TT p );
     288forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( T arr[] );
     289forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT | { void adelete( TT ); } ) void adelete( T arr[], TT rest );
    191290
    192291//---------------------------------------
     
    254353extern "C" {                                                                                    // override C version
    255354        void srandom( unsigned int seed );
    256         long int random( void );
     355        long int random( void );                                                        // GENERATES POSITIVE AND NEGATIVE VALUES
     356        // For positive values, use unsigned int, e.g., unsigned int r = random() % 100U;
    257357} // extern "C"
    258358
     
    261361        long int random( long int u ) { if ( u < 0 ) return random( u, 0 ); else return random( 0, u ); } // [0,u)
    262362        unsigned long int random( void ) { return lrand48(); }
     363        unsigned long int random( unsigned long int u ) { return lrand48() % u; } // [0,u)
    263364        unsigned long int random( unsigned long int l, unsigned long int u ) { if ( u < l ) [u, l] = [l, u]; return lrand48() % (u - l) + l; } // [l,u)
    264         unsigned long int random( unsigned long int u ) { return lrand48() % u; } // [0,u)
    265365
    266366        char random( void ) { return (unsigned long int)random(); }
     
    283383//---------------------------------------
    284384
    285 #include "common.hfa"
    286 
    287 //---------------------------------------
    288 
    289 extern bool threading_enabled(void) OPTIONAL_THREAD;
     385extern bool threading_enabled( void ) OPTIONAL_THREAD;
    290386
    291387// Local Variables: //
  • libcfa/src/time.hfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed Mar 14 23:18:57 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  4 08:24:32 2020
    13 // Update Count     : 654
     12// Last Modified On : Wed Jun 17 16:13:00 2020
     13// Update Count     : 663
    1414//
    1515
     
    2020
    2121#include <time.h>                                                                               // timespec
    22 extern "C" {
    2322#include <sys/time.h>                                                                   // timeval
    24 }
    2523#include <time_t.hfa>                                                                   // Duration/Time types
    2624
     
    9189        int64_t ?`w( Duration dur ) { return dur.tn / (7LL * 24LL * 60LL * 60LL * TIMEGRAN); }
    9290
     91        double ?`dns( Duration dur ) { return dur.tn; }
     92        double ?`dus( Duration dur ) { return dur.tn / ((double)TIMEGRAN / 1_000_000.); }
     93        double ?`dms( Duration dur ) { return dur.tn / ((double)TIMEGRAN / 1_000.); }
     94        double ?`ds( Duration dur ) { return dur.tn / (double)TIMEGRAN; }
     95        double ?`dm( Duration dur ) { return dur.tn / (60. * TIMEGRAN); }
     96        double ?`dh( Duration dur ) { return dur.tn / (60. * 60. * (double)TIMEGRAN); }
     97        double ?`dd( Duration dur ) { return dur.tn / (24. * 60. * 60. * (double)TIMEGRAN); }
     98        double ?`dw( Duration dur ) { return dur.tn / (7. * 24. * 60. * 60. * (double)TIMEGRAN); }
     99
    93100        Duration max( Duration lhs, Duration rhs ) { return  (lhs.tn < rhs.tn) ? rhs : lhs;}
    94101        Duration min( Duration lhs, Duration rhs ) { return !(rhs.tn < lhs.tn) ? lhs : rhs;}
  • longrun_tests/Makefile.am

    r3c64c668 r58fe85a  
    1818ACLOCAL_AMFLAGS  = -I automake
    1919
    20 include $(top_srcdir)/src/cfa.make
     20include $(top_srcdir)/tools/build/cfa.make
    2121
    2222repeats=10
     
    4444        -DTEST_$(shell cat .type | tr a-z A-Z)
    4545
    46 TESTS = block coroutine create disjoint enter enter3 processor stack wait yield
     46TESTS = block coroutine create disjoint enter enter3 locks processor stack wait yield
    4747
    4848# .INTERMEDIATE: $(TESTS)
  • src/AST/Attribute.hpp

    r3c64c668 r58fe85a  
    5151        template<typename node_t>
    5252        friend node_t * mutate(const node_t * node);
     53        template<typename node_t>
     54    friend node_t * shallowCopy(const node_t * node);
    5355};
    5456
  • src/AST/CVQualifiers.hpp

    r3c64c668 r58fe85a  
    2727                Restrict = 1 << 1,
    2828                Volatile = 1 << 2,
    29                 Lvalue   = 1 << 3,
    30                 Mutex    = 1 << 4,
    31                 Atomic   = 1 << 5,
    32                 NumQualifiers = 6
     29                Mutex    = 1 << 3,
     30                Atomic   = 1 << 4,
     31                NumQualifiers = 5
    3332        };
    3433
    3534        /// Mask for equivalence-preserving qualfiers
    36         enum { EquivQualifiers = ~(Restrict | Lvalue) };
     35        enum { EquivQualifiers = ~Restrict };
    3736
    3837        /// Underlying data for qualifiers
     
    4443                                bool is_restrict : 1;
    4544                                bool is_volatile : 1;
    46                                 bool is_lvalue   : 1;
    4745                                bool is_mutex    : 1;
    4846                                bool is_atomic   : 1;
  • src/AST/Convert.cpp

    r3c64c668 r58fe85a  
    99// Author           : Thierry Delisle
    1010// Created On       : Thu May 09 15::37::05 2019
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Dec 11 21:39:32 2019
    13 // Update Count     : 33
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Thr Nov 12 10:07:00 2020
     13// Update Count     : 34
    1414//
    1515
     
    2020
    2121#include "AST/Attribute.hpp"
     22#include "AST/Copy.hpp"
    2223#include "AST/Decl.hpp"
    2324#include "AST/Expr.hpp"
    2425#include "AST/Init.hpp"
    2526#include "AST/Stmt.hpp"
     27#include "AST/TranslationUnit.hpp"
    2628#include "AST/TypeSubstitution.hpp"
    2729
     
    4648
    4749//================================================================================================
    48 namespace {
     50namespace ast {
    4951
    5052// This is to preserve the FindSpecialDecls hack. It does not (and perhaps should not)
    5153// allow us to use the same stratagy in the new ast.
    52 ast::Type * sizeType = nullptr;
    53 ast::FunctionDecl * dereferenceOperator = nullptr;
    54 ast::StructDecl   * dtorStruct = nullptr;
    55 ast::FunctionDecl * dtorStructDestroy = nullptr;
     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;
    5661
    5762}
     
    6267        using Cache = std::unordered_map< const ast::Node *, BaseSyntaxNode * >;
    6368        Cache cache;
     69
     70        // Statements can no longer be shared.
     71        // however, since StmtExprResult is now implemented, need to still maintain
     72        // readonly references.
     73        Cache readonlyCache;
    6474
    6575        template<typename T>
     
    153163        }
    154164
    155         const ast::DeclWithType * visit( const ast::ObjectDecl * node ) override final {
    156                 auto&& bfwd = get<Expression>().accept1( node->bitfieldWidth );
    157                 auto&& type = get<Type>().accept1( node->type );
    158                 auto&& init = get<Initializer>().accept1( node->init );
    159                 auto&& attr = get<Attribute>().acceptL( node->attributes );
     165        const ast::DeclWithType * visit( const ast::ObjectDecl * node ) override final {       
    160166                if ( inCache( node ) ) {
    161167                        return nullptr;
    162168                }
     169                auto bfwd = get<Expression>().accept1( node->bitfieldWidth );
     170                auto type = get<Type>().accept1( node->type );
     171                auto attr = get<Attribute>().acceptL( node->attributes );
     172
    163173                auto decl = new ObjectDecl(
    164174                        node->name,
     
    166176                        LinkageSpec::Spec( node->linkage.val ),
    167177                        bfwd,
    168                         type,
    169                         init,
     178                        type->clone(),
     179                        nullptr, // prevent infinite loop
    170180                        attr,
    171181                        Type::FuncSpecifiers( node->funcSpec.val )
    172182                );
    173                 return declWithTypePostamble( decl, node );
     183
     184                // handles the case where node->init references itself
     185                // xxx - does it really happen?
     186                declWithTypePostamble(decl, node);
     187                auto init = get<Initializer>().accept1( node->init );
     188                decl->init = init;
     189
     190                this->node = decl;
     191                return nullptr;
    174192        }
    175193
    176194        const ast::DeclWithType * visit( const ast::FunctionDecl * node ) override final {
    177195                if ( inCache( node ) ) return nullptr;
     196
     197                // function decl contains real variables that the type must use.
     198                // the structural change means function type in and out of decl
     199                // must be handled **differently** on convert back to old.
     200                auto ftype = new FunctionType(
     201                        cv(node->type),
     202                        (bool)node->type->isVarArgs
     203                );
     204                ftype->returnVals = get<DeclarationWithType>().acceptL(node->returns);
     205                ftype->parameters = get<DeclarationWithType>().acceptL(node->params);
     206
     207                ftype->forall = get<TypeDecl>().acceptL( node->type_params );
     208                if (!node->assertions.empty()) {
     209                        assert(!ftype->forall.empty());
     210                        // find somewhere to place assertions back, for convenience it is the last slot
     211                        ftype->forall.back()->assertions = get<DeclarationWithType>().acceptL(node->assertions);
     212                }
     213
     214                visitType(node->type, ftype);
     215
    178216                auto decl = new FunctionDecl(
    179217                        node->name,
    180218                        Type::StorageClasses( node->storage.val ),
    181219                        LinkageSpec::Spec( node->linkage.val ),
    182                         get<FunctionType>().accept1( node->type ),
     220                        ftype,
     221                        //get<FunctionType>().accept1( node->type ),
    183222                        {},
    184223                        get<Attribute>().acceptL( node->attributes ),
     
    188227                decl->statements = get<CompoundStmt>().accept1( node->stmts );
    189228                decl->withExprs = get<Expression>().acceptL( node->withExprs );
    190                 if ( dereferenceOperator == node ) {
     229                if ( ast::dereferenceOperator == node ) {
    191230                        Validate::dereferenceOperator = decl;
    192231                }
    193                 if ( dtorStructDestroy == node ) {
     232                if ( ast::dtorStructDestroy == node ) {
    194233                        Validate::dtorStructDestroy = decl;
    195234                }
     
    199238        const ast::Decl * namedTypePostamble( NamedTypeDecl * decl, const ast::NamedTypeDecl * node ) {
    200239                // base comes from constructor
    201                 decl->parameters = get<TypeDecl>().acceptL( node->params );
    202240                decl->assertions = get<DeclarationWithType>().acceptL( node->assertions );
    203241                declPostamble( decl, node );
     
    250288                );
    251289
    252                 if ( dtorStruct == node ) {
     290                if ( ast::dtorStruct == node ) {
    253291                        Validate::dtorStruct = decl;
    254292                }
     
    303341
    304342        const ast::Stmt * stmtPostamble( Statement * stmt, const ast::Stmt * node ) {
    305                 cache.emplace( node, stmt );
     343                // force statements in old tree to be unique.
     344                // cache.emplace( node, stmt );
     345                readonlyCache.emplace( node, stmt );
    306346                stmt->location = node->location;
    307347                stmt->labels = makeLabelL( stmt, node->labels );
     
    320360                if ( inCache( node ) ) return nullptr;
    321361                auto stmt = new ExprStmt( nullptr );
    322                 cache.emplace( node, stmt );
    323362                stmt->expr = get<Expression>().accept1( node->expr );
    324363                return stmtPostamble( stmt, node );
     
    493532        }
    494533
     534        const ast::Stmt * visit(const ast::SuspendStmt * node ) override final {
     535                if ( inCache( node ) ) return nullptr;
     536                auto stmt = new SuspendStmt();
     537                stmt->then   = get<CompoundStmt>().accept1( node->then   );
     538                switch(node->type) {
     539                        case ast::SuspendStmt::None     : stmt->type = SuspendStmt::None     ; break;
     540                        case ast::SuspendStmt::Coroutine: stmt->type = SuspendStmt::Coroutine; break;
     541                        case ast::SuspendStmt::Generator: stmt->type = SuspendStmt::Generator; break;
     542                }
     543                return stmtPostamble( stmt, node );
     544        }
     545
    495546        const ast::Stmt * visit( const ast::WaitForStmt * node ) override final {
    496547                if ( inCache( node ) ) return nullptr;
     
    556607
    557608                for (decltype(src->begin()) src_i = src->begin(); src_i != src->end(); src_i++) {
    558                         rslt->add( src_i->first,
     609                        rslt->add( src_i->first.typeString(),
    559610                                   get<Type>().accept1(src_i->second) );
    560                 }
    561 
    562                 for (decltype(src->beginVar()) src_i = src->beginVar(); src_i != src->endVar(); src_i++) {
    563                         rslt->addVar( src_i->first,
    564                                       get<Expression>().accept1(src_i->second) );
    565611                }
    566612
     
    575621                assert( tgtResnSlots.empty() );
    576622
    577                 if ( srcInferred.mode == ast::Expr::InferUnion::Params ) {
     623                if ( srcInferred.data.inferParams ) {
    578624                        const ast::InferredParams &srcParams = srcInferred.inferParams();
    579625                        for (auto & srcParam : srcParams) {
     
    581627                                        srcParam.second.decl,
    582628                                        get<Declaration>().accept1(srcParam.second.declptr),
    583                                         get<Type>().accept1(srcParam.second.actualType),
    584                                         get<Type>().accept1(srcParam.second.formalType),
    585                                         get<Expression>().accept1(srcParam.second.expr)
     629                                        get<Type>().accept1(srcParam.second.actualType)->clone(),
     630                                        get<Type>().accept1(srcParam.second.formalType)->clone(),
     631                                        get<Expression>().accept1(srcParam.second.expr)->clone()
    586632                                ));
    587633                                assert(res.second);
    588634                        }
    589                 } else if ( srcInferred.mode == ast::Expr::InferUnion::Slots  ) {
     635                }
     636                if ( srcInferred.data.resnSlots ) {
    590637                        const ast::ResnSlots &srcSlots = srcInferred.resnSlots();
    591638                        for (auto srcSlot : srcSlots) {
     
    608655
    609656                tgt->result = get<Type>().accept1(src->result);
     657                // Unconditionally use a clone of the result type.
     658                // We know this will leak some objects: much of the immediate conversion result.
     659                // In some cases, using the conversion result directly gives unintended object sharing.
     660                // A parameter (ObjectDecl, a child of a FunctionType) is shared by the weak-ref cache.
     661                // But tgt->result must be fully owned privately by tgt.
     662                // Applying these conservative copies here means
     663                // - weak references point at the declaration's copy, not these expr.result copies (good)
     664                // - we copy more objects than really needed (bad, tolerated)
     665                if (tgt->result) {
     666                        tgt->result = tgt->result->clone();
     667                }
    610668                return visitBaseExpr_skipResultType(src, tgt);
    611669        }
     
    680738                        new KeywordCastExpr(
    681739                                get<Expression>().accept1(node->arg),
    682                                 castTarget
     740                                castTarget,
     741                                {node->concrete_target.field, node->concrete_target.getter}
    683742                        )
    684743                );
     
    9671026
    9681027        const ast::Expr * visit( const ast::StmtExpr * node ) override final {
     1028                auto stmts = node->stmts;
     1029                // disable sharing between multiple StmtExprs explicitly.
     1030                // this should no longer be true.
     1031
    9691032                auto rslt = new StmtExpr(
    970                         get<CompoundStmt>().accept1(node->stmts)
     1033                        get<CompoundStmt>().accept1(stmts)
    9711034                );
    9721035
    9731036                rslt->returnDecls = get<ObjectDecl>().acceptL(node->returnDecls);
    9741037                rslt->dtors       = get<Expression>().acceptL(node->dtors);
     1038                if (node->resultExpr) {
     1039                        // this MUST be found by children visit
     1040                        rslt->resultExpr  = strict_dynamic_cast<ExprStmt *>(readonlyCache.at(node->resultExpr));
     1041                }
    9751042
    9761043                auto expr = visitBaseExpr( node, rslt );
     
    9891056
    9901057                auto expr = visitBaseExpr( node, rslt );
    991                 this->node = expr;
     1058                this->node = expr->clone();
    9921059                return nullptr;
    9931060        }
     
    10791146                auto type = new BasicType{ cv( node ), (BasicType::Kind)(unsigned)node->kind };
    10801147                // I believe this should always be a BasicType.
    1081                 if ( sizeType == node ) {
     1148                if ( ast::sizeType == node ) {
    10821149                        Validate::SizeType = type;
    10831150                }
     
    11211188
    11221189        const ast::Type * visit( const ast::FunctionType * node ) override final {
     1190                static std::string dummy_paramvar_prefix = "__param_";
     1191                static std::string dummy_returnvar_prefix = "__retval_";
     1192
    11231193                auto ty = new FunctionType {
    11241194                        cv( node ),
    11251195                        (bool)node->isVarArgs
    11261196                };
    1127                 ty->returnVals = get<DeclarationWithType>().acceptL( node->returns );
    1128                 ty->parameters = get<DeclarationWithType>().acceptL( node->params );
    1129                 ty->forall = get<TypeDecl>().acceptL( node->forall );
     1197                auto returns = get<Type>().acceptL(node->returns);
     1198                auto params = get<Type>().acceptL(node->params);
     1199
     1200                int ret_index = 0;
     1201                for (auto t: returns) {
     1202                        // xxx - LinkageSpec shouldn't matter but needs to be something
     1203                        ObjectDecl * dummy = new ObjectDecl(dummy_returnvar_prefix + std::to_string(ret_index++), {}, LinkageSpec::C, nullptr, t, nullptr);
     1204                        ty->returnVals.push_back(dummy);
     1205                }
     1206                int param_index = 0;
     1207                for (auto t: params) {
     1208                        ObjectDecl * dummy = new ObjectDecl(dummy_paramvar_prefix + std::to_string(param_index++), {}, LinkageSpec::C, nullptr, t, nullptr);
     1209                        ty->parameters.push_back(dummy);
     1210                }
     1211
     1212                // ty->returnVals = get<DeclarationWithType>().acceptL( node->returns );
     1213                // ty->parameters = get<DeclarationWithType>().acceptL( node->params );
     1214
     1215                auto types = get<TypeInstType>().acceptL( node->forall );
     1216                for (auto t : types) {
     1217                        auto newT = new TypeDecl(*t->baseType);
     1218                        newT->name = t->name; // converted by typeString()
     1219                        for (auto asst : newT->assertions) delete asst;
     1220                        newT->assertions.clear();
     1221                        ty->forall.push_back(newT);
     1222                }
     1223                auto assts = get<VariableExpr>().acceptL( node->assertions );
     1224                if (!assts.empty()) {
     1225                        assert(!types.empty());
     1226                        for (auto asst : assts) {
     1227                                auto newDecl = new ObjectDecl(*strict_dynamic_cast<ObjectDecl*>(asst->var));
     1228                                delete newDecl->type;
     1229                                newDecl->type = asst->result->clone();
     1230                                newDecl->storageClasses.is_extern = true; // hack
     1231                                ty->forall.back()->assertions.push_back(newDecl);
     1232                        }
     1233                }
     1234
    11301235                return visitType( node, ty );
    11311236        }
    11321237
    1133         const ast::Type * postvisit( const ast::ReferenceToType * old, ReferenceToType * ty ) {
    1134                 ty->forall = get<TypeDecl>().acceptL( old->forall );
     1238        const ast::Type * postvisit( const ast::BaseInstType * old, ReferenceToType * ty ) {
    11351239                ty->parameters = get<Expression>().acceptL( old->params );
    11361240                ty->hoistType = old->hoistType;
     
    12151319                        ty = new TypeInstType{
    12161320                                cv( node ),
    1217                                 node->name,
     1321                                node->typeString(),
    12181322                                get<TypeDecl>().accept1( node->base ),
    12191323                                get<Attribute>().acceptL( node->attributes )
     
    12221326                        ty = new TypeInstType{
    12231327                                cv( node ),
    1224                                 node->name,
     1328                                node->typeString(),
    12251329                                node->kind == ast::TypeDecl::Ftype,
    12261330                                get<Attribute>().acceptL( node->attributes )
     
    13191423};
    13201424
    1321 std::list< Declaration * > convert( const std::list< ast::ptr< ast::Decl > > && translationUnit ) {
     1425std::list< Declaration * > convert( const ast::TranslationUnit && translationUnit ) {
    13221426        ConverterNewToOld c;
    13231427        std::list< Declaration * > decls;
    1324         for(auto d : translationUnit) {
     1428        for(auto d : translationUnit.decls) {
    13251429                decls.emplace_back( c.decl( d ) );
    13261430        }
     
    13431447        ast::Node * node = nullptr;
    13441448        /// cache of nodes that might be referenced by readonly<> for de-duplication
    1345         std::unordered_map< const BaseSyntaxNode *, ast::Node * > cache = {};
     1449        /// in case that some nodes are dropped by conversion (due to possible structural change)
     1450        /// use smart pointers in cache value to prevent accidental invalidation.
     1451        /// at conversion stage, all created nodes are guaranteed to be unique, therefore
     1452        /// const_casting out of smart pointers is permitted.
     1453        std::unordered_map< const BaseSyntaxNode *, ast::readonly<ast::Node> > cache = {};
    13461454
    13471455        // Local Utilities:
     
    14161524                auto it = cache.find( old );
    14171525                if ( it == cache.end() ) return false;
    1418                 node = it->second;
     1526                node = const_cast<ast::Node *>(it->second.get());
    14191527                return true;
    14201528        }
     
    14551563        virtual void visit( const FunctionDecl * old ) override final {
    14561564                if ( inCache( old ) ) return;
     1565                auto paramVars = GET_ACCEPT_V(type->parameters, DeclWithType);
     1566                auto returnVars = GET_ACCEPT_V(type->returnVals, DeclWithType);
     1567                auto forall = GET_ACCEPT_V(type->forall, TypeDecl);
     1568
     1569                // function type is now derived from parameter decls instead of storing them
     1570
     1571                /*
     1572                auto ftype = new ast::FunctionType((ast::ArgumentFlag)old->type->isVarArgs, cv(old->type));
     1573                ftype->params.reserve(paramVars.size());
     1574                ftype->returns.reserve(returnVars.size());
     1575
     1576                for (auto & v: paramVars) {
     1577                        ftype->params.emplace_back(v->get_type());
     1578                }
     1579                for (auto & v: returnVars) {
     1580                        ftype->returns.emplace_back(v->get_type());
     1581                }
     1582                ftype->forall = std::move(forall);
     1583                */
     1584
     1585                // can function type have attributes? seems not to be the case.
     1586                // visitType(old->type, ftype);
     1587
     1588                // collect assertions and put directly in FunctionDecl
     1589                std::vector<ast::ptr<ast::DeclWithType>> assertions;
     1590                for (auto & param: forall) {
     1591                        for (auto & asst: param->assertions) {
     1592                                assertf(asst->unique(), "newly converted decl must be unique");
     1593                                assertions.emplace_back(asst);
     1594                        }
     1595                        auto mut = param.get_and_mutate();
     1596                        assertf(mut == param, "newly converted decl must be unique");
     1597                        mut->assertions.clear();
     1598                }
     1599
    14571600                auto decl = new ast::FunctionDecl{
    14581601                        old->location,
    14591602                        old->name,
    1460                         GET_ACCEPT_1(type, FunctionType),
     1603                        // GET_ACCEPT_1(type, FunctionType),
     1604                        std::move(forall),
     1605                        std::move(paramVars),
     1606                        std::move(returnVars),
    14611607                        {},
    14621608                        { old->storageClasses.val },
    14631609                        { old->linkage.val },
    14641610                        GET_ACCEPT_V(attributes, Attribute),
    1465                         { old->get_funcSpec().val }
     1611                        { old->get_funcSpec().val },
     1612                        old->type->isVarArgs
    14661613                };
     1614
     1615                // decl->type = ftype;
    14671616                cache.emplace( old, decl );
     1617
     1618                decl->assertions = std::move(assertions);
    14681619                decl->withExprs = GET_ACCEPT_V(withExprs, Expr);
    14691620                decl->stmts = GET_ACCEPT_1(statements, CompoundStmt);
     
    14781629
    14791630                if ( Validate::dereferenceOperator == old ) {
    1480                         dereferenceOperator = decl;
     1631                        ast::dereferenceOperator = decl;
    14811632                }
    14821633
    14831634                if ( Validate::dtorStructDestroy == old ) {
    1484                         dtorStructDestroy = decl;
     1635                        ast::dtorStructDestroy = decl;
    14851636                }
    14861637        }
     
    15071658
    15081659                if ( Validate::dtorStruct == old ) {
    1509                         dtorStruct = decl;
     1660                        ast::dtorStruct = decl;
    15101661                }
    15111662        }
     
    15841735                cache.emplace( old, decl );
    15851736                decl->assertions = GET_ACCEPT_V(assertions, DeclWithType);
    1586                 decl->params     = GET_ACCEPT_V(parameters, TypeDecl);
    15871737                decl->extension  = old->extension;
    15881738                decl->uniqueId   = old->uniqueId;
     
    16001750                );
    16011751                decl->assertions = GET_ACCEPT_V(assertions, DeclWithType);
    1602                 decl->params     = GET_ACCEPT_V(parameters, TypeDecl);
    16031752                decl->extension  = old->extension;
    16041753                decl->uniqueId   = old->uniqueId;
     
    18592008        }
    18602009
     2010        virtual void visit( const SuspendStmt * old ) override final {
     2011                if ( inCache( old ) ) return;
     2012                ast::SuspendStmt::Type type;
     2013                switch (old->type) {
     2014                        case SuspendStmt::Coroutine: type = ast::SuspendStmt::Coroutine; break;
     2015                        case SuspendStmt::Generator: type = ast::SuspendStmt::Generator; break;
     2016                        case SuspendStmt::None     : type = ast::SuspendStmt::None     ; break;
     2017                        default: abort();
     2018                }
     2019                this->node = new ast::SuspendStmt(
     2020                        old->location,
     2021                        GET_ACCEPT_1(then  , CompoundStmt),
     2022                        type,
     2023                        GET_LABELS_V(old->labels)
     2024                );
     2025                cache.emplace( old, this->node );
     2026        }
     2027
    18612028        virtual void visit( const WaitForStmt * old ) override final {
    18622029                if ( inCache( old ) ) return;
     
    19322099        }
    19332100
     2101        // TypeSubstitution shouldn't exist yet in old.
    19342102        ast::TypeSubstitution * convertTypeSubstitution(const TypeSubstitution * old) {
    1935 
     2103               
    19362104                if (!old) return nullptr;
    1937 
     2105                if (old->empty()) return nullptr;
     2106                assert(false);
     2107
     2108                /*
    19382109                ast::TypeSubstitution *rslt = new ast::TypeSubstitution();
    19392110
     
    19432114                }
    19442115
    1945                 for (decltype(old->beginVar()) old_i = old->beginVar(); old_i != old->endVar(); old_i++) {
    1946                         rslt->addVar( old_i->first,
    1947                                       getAccept1<ast::Expr>(old_i->second) );
    1948                 }
    1949 
    19502116                return rslt;
     2117                */
    19512118        }
    19522119
     
    19562123
    19572124                assert( oldInferParams.empty() || oldResnSlots.empty() );
    1958                 assert( newInferred.mode == ast::Expr::InferUnion::Empty );
     2125                // assert( newInferred.mode == ast::Expr::InferUnion::Empty );
    19592126
    19602127                if ( !oldInferParams.empty() ) {
     
    20392206                                old->location,
    20402207                                GET_ACCEPT_1(arg, Expr),
    2041                                 castTarget
     2208                                castTarget,
     2209                                {old->concrete_target.field, old->concrete_target.getter}
    20422210                        )
    20432211                );
     
    20872255                                old->location,
    20882256                                GET_ACCEPT_1(member, DeclWithType),
    2089                                 GET_ACCEPT_1(aggregate, Expr)
     2257                                GET_ACCEPT_1(aggregate, Expr),
     2258                                ast::MemberExpr::NoOpConstructionChosen
    20902259                        )
    20912260                );
     
    24192588                // I believe this should always be a BasicType.
    24202589                if ( Validate::SizeType == old ) {
    2421                         sizeType = type;
     2590                        ast::sizeType = type;
    24222591                }
    24232592                visitType( old, type );
     
    24642633                        cv( old )
    24652634                };
    2466                 ty->returns = GET_ACCEPT_V( returnVals, DeclWithType );
    2467                 ty->params = GET_ACCEPT_V( parameters, DeclWithType );
    2468                 ty->forall = GET_ACCEPT_V( forall, TypeDecl );
     2635                auto returnVars = GET_ACCEPT_V(returnVals, DeclWithType);
     2636                auto paramVars = GET_ACCEPT_V(parameters, DeclWithType);
     2637                // ty->returns = GET_ACCEPT_V( returnVals, DeclWithType );
     2638                // ty->params = GET_ACCEPT_V( parameters, DeclWithType );
     2639                for (auto & v: returnVars) {
     2640                        ty->returns.emplace_back(v->get_type());
     2641                }
     2642                for (auto & v: paramVars) {
     2643                        ty->params.emplace_back(v->get_type());
     2644                }
     2645                // xxx - when will this be non-null?
     2646                // will have to create dangling (no-owner) decls to be pointed to
     2647                auto foralls = GET_ACCEPT_V( forall, TypeDecl );
     2648
     2649                for (auto & param : foralls) {
     2650                        ty->forall.emplace_back(new ast::TypeInstType(param->name, param));
     2651                        for (auto asst : param->assertions) {
     2652                                ty->assertions.emplace_back(new ast::VariableExpr({}, asst));
     2653                        }
     2654                }
    24692655                visitType( old, ty );
    24702656        }
    24712657
    2472         void postvisit( const ReferenceToType * old, ast::ReferenceToType * ty ) {
    2473                 ty->forall = GET_ACCEPT_V( forall, TypeDecl );
     2658        void postvisit( const ReferenceToType * old, ast::BaseInstType * ty ) {
    24742659                ty->params = GET_ACCEPT_V( parameters, Expr );
    24752660                ty->hoistType = old->hoistType;
     
    26162801                        old->location,
    26172802                        GET_ACCEPT_1(value, Expr),
    2618                         (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast::DoConstruct
     2803                        (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast::NoConstruct
    26192804                );
    26202805        }
     
    26252810                        GET_ACCEPT_V(initializers, Init),
    26262811                        GET_ACCEPT_V(designations, Designation),
    2627                         (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast::DoConstruct
     2812                        (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast::NoConstruct
    26282813                );
    26292814        }
     
    26562841#undef GET_ACCEPT_1
    26572842
    2658 std::list< ast::ptr< ast::Decl > > convert( const std::list< Declaration * > && translationUnit ) {
     2843ast::TranslationUnit convert( const std::list< Declaration * > && translationUnit ) {
    26592844        ConverterOldToNew c;
    2660         std::list< ast::ptr< ast::Decl > > decls;
     2845        ast::TranslationUnit unit;
     2846        if (Validate::SizeType) {
     2847                // this should be a BasicType.
     2848                auto old = strict_dynamic_cast<BasicType *>(Validate::SizeType);
     2849                ast::sizeType = new ast::BasicType{ (ast::BasicType::Kind)(unsigned)old->kind };
     2850        }
     2851
    26612852        for(auto d : translationUnit) {
    26622853                d->accept( c );
    2663                 decls.emplace_back( c.decl() );
     2854                unit.decls.emplace_back( c.decl() );
    26642855        }
    26652856        deleteAll(translationUnit);
    2666         return decls;
     2857
     2858        // Load the local static varables into the global store.
     2859        unit.global.sizeType = ast::sizeType;
     2860        unit.global.dereference = ast::dereferenceOperator;
     2861        unit.global.dtorStruct = ast::dtorStruct;
     2862        unit.global.dtorDestroy = ast::dtorStructDestroy;
     2863
     2864        return unit;
    26672865}
  • src/AST/Convert.hpp

    r3c64c668 r58fe85a  
    1818#include <list>
    1919
    20 #include "AST/Node.hpp"
    21 
    2220class Declaration;
    2321namespace ast {
    24         class Decl;
     22        struct TranslationUnit;
    2523};
    2624
    27 std::list< Declaration * > convert( const std::list< ast::ptr< ast::Decl > > && translationUnit );
    28 std::list< ast::ptr< ast::Decl > > convert( const std::list< Declaration * > && translationUnit );
     25std::list< Declaration * > convert( const ast::TranslationUnit && translationUnit );
     26ast::TranslationUnit convert( const std::list< Declaration * > && translationUnit );
  • src/AST/Decl.cpp

    r3c64c668 r58fe85a  
    4949// --- FunctionDecl
    5050
     51FunctionDecl::FunctionDecl( const CodeLocation & loc, const std::string & name,
     52        std::vector<ptr<TypeDecl>>&& forall,
     53        std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
     54        CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,
     55        std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)
     56: DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
     57        type_params(std::move(forall)), stmts( stmts ) {
     58        FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs));
     59        for (auto & param : this->params) {
     60                ftype->params.emplace_back(param->get_type());
     61        }
     62        for (auto & ret : this->returns) {
     63                ftype->returns.emplace_back(ret->get_type());
     64        }
     65        for (auto & tp : this->type_params) {
     66                ftype->forall.emplace_back(new TypeInstType(tp->name, tp));
     67        }
     68        this->type = ftype;
     69}
     70
     71
    5172const Type * FunctionDecl::get_type() const { return type.get(); }
    52 void FunctionDecl::set_type(Type * t) { type = strict_dynamic_cast< FunctionType* >( t ); }
     73void FunctionDecl::set_type( const Type * t ) {
     74        type = strict_dynamic_cast< const FunctionType * >( t );
     75}
    5376
    5477// --- TypeDecl
  • src/AST/Decl.hpp

    r3c64c668 r58fe85a  
    3333
    3434// Must be included in *all* AST classes; should be #undef'd at the end of the file
    35 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
     35#define MUTATE_FRIEND \
     36    template<typename node_t> friend node_t * mutate(const node_t * node); \
     37        template<typename node_t> friend node_t * shallowCopy(const node_t * node);
    3638
    3739namespace ast {
     
    7779        ptr<Expr> asmName;
    7880        bool isDeleted = false;
     81        bool isTypeFixed = false;
    7982
    8083        DeclWithType( const CodeLocation& loc, const std::string& name, Storage::Classes storage,
     
    8891        virtual const Type * get_type() const = 0;
    8992        /// Set type of this declaration. May be verified by subclass
    90         virtual void set_type(Type *) = 0;
     93        virtual void set_type( const Type * ) = 0;
    9194
    9295        const DeclWithType * accept( Visitor & v ) const override = 0;
     
    111114
    112115        const Type* get_type() const override { return type; }
    113         void set_type( Type * ty ) override { type = ty; }
     116        void set_type( const Type * ty ) override { type = ty; }
    114117
    115118        const DeclWithType * accept( Visitor& v ) const override { return v.visit( this ); }
     
    122125class FunctionDecl : public DeclWithType {
    123126public:
     127        std::vector<ptr<DeclWithType>> params;
     128        std::vector<ptr<DeclWithType>> returns;
     129        std::vector<ptr<TypeDecl>> type_params;
     130        std::vector<ptr<DeclWithType>> assertions;
     131        // declared type, derived from parameter declarations
    124132        ptr<FunctionType> type;
    125133        ptr<CompoundStmt> stmts;
    126134        std::vector< ptr<Expr> > withExprs;
    127135
    128         FunctionDecl( const CodeLocation & loc, const std::string & name, FunctionType * type,
     136
     137        FunctionDecl( const CodeLocation & loc, const std::string & name, std::vector<ptr<TypeDecl>>&& forall,
     138                std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
    129139                CompoundStmt * stmts, Storage::Classes storage = {}, Linkage::Spec linkage = Linkage::C,
    130                 std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {})
    131         : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), type( type ),
    132           stmts( stmts ) {}
     140                std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {}, bool isVarArgs = false);
     141        // : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
     142        //  stmts( stmts ) {}
    133143
    134144        const Type * get_type() const override;
    135         void set_type(Type * t) override;
     145        void set_type( const Type * t ) override;
    136146
    137147        bool has_body() const { return stmts; }
     
    147157public:
    148158        ptr<Type> base;
    149         std::vector<ptr<TypeDecl>> params;
    150159        std::vector<ptr<DeclWithType>> assertions;
    151160
    152         NamedTypeDecl( const CodeLocation& loc, const std::string& name, Storage::Classes storage,
    153                 Type* b, Linkage::Spec spec = Linkage::Cforall )
    154         : Decl( loc, name, storage, spec ), base( b ), params(), assertions() {}
     161        NamedTypeDecl(
     162                const CodeLocation & loc, const std::string & name, Storage::Classes storage,
     163                const Type * b, Linkage::Spec spec = Linkage::Cforall )
     164        : Decl( loc, name, storage, spec ), base( b ), assertions() {}
    155165
    156166        /// Produces a name for the kind of alias
     
    186196        };
    187197
    188         TypeDecl( const CodeLocation & loc, const std::string & name, Storage::Classes storage, Type * b,
    189                           Kind k, bool s, Type * i = nullptr )
    190                 : NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == Ttype || s ),
    191                 init( i ) {}
     198        TypeDecl(
     199                const CodeLocation & loc, const std::string & name, Storage::Classes storage,
     200                const Type * b, TypeDecl::Kind k, bool s, const Type * i = nullptr )
     201        : NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == TypeDecl::Ttype || s ),
     202          init( i ) {}
    192203
    193204        const char * typeString() const override;
     
    259270
    260271        bool is_coroutine() { return kind == Coroutine; }
    261         bool is_monitor() { return kind == Monitor; }
    262         bool is_thread() { return kind == Thread; }
     272        bool is_generator() { return kind == Generator; }
     273        bool is_monitor  () { return kind == Monitor  ; }
     274        bool is_thread   () { return kind == Thread   ; }
    263275
    264276        const Decl * accept( Visitor & v ) const override { return v.visit( this ); }
  • src/AST/DeclReplacer.cpp

    r3c64c668 r58fe85a  
    3838                        const ast::TypeInstType * previsit( const ast::TypeInstType * );
    3939                };
     40
     41                struct VarExprReplacer {
     42                private:
     43                        const ExprMap & exprMap;
     44                       
     45                public:
     46                        VarExprReplacer(const ExprMap & exprMap): exprMap (exprMap) {}
     47
     48                        const Expr * postvisit (const VariableExpr *);
     49                };
    4050        }
    4151
     
    5464                DeclMap declMap;
    5565                return replace( node, declMap, typeMap, debug );
     66        }
     67
     68        const ast::Node * replace( const ast::Node * node, const ExprMap & exprMap) {
     69                Pass<VarExprReplacer> replacer = {exprMap};
     70                return node->accept( replacer );
    5671        }
    5772
     
    88103                        return ninst;
    89104                }
     105
     106                const Expr * VarExprReplacer::postvisit( const VariableExpr * expr ) {
     107                        if (!exprMap.count(expr->var)) return expr;
     108
     109                        return exprMap.at(expr->var);
     110                }
     111
    90112        }
    91113}
  • src/AST/DeclReplacer.hpp

    r3c64c668 r58fe85a  
    2323        class DeclWithType;
    2424        class TypeDecl;
     25        class Expr;
    2526
    2627        namespace DeclReplacer {
    2728                using DeclMap = std::unordered_map< const DeclWithType *, const DeclWithType * >;
    2829                using TypeMap = std::unordered_map< const TypeDecl *, const TypeDecl * >;
     30                using ExprMap = std::unordered_map< const DeclWithType *, const Expr * >;
    2931
    3032                const Node * replace( const Node * node, const DeclMap & declMap, bool debug = false );
    3133                const Node * replace( const Node * node, const TypeMap & typeMap, bool debug = false );
    3234                const Node * replace( const Node * node, const DeclMap & declMap, const TypeMap & typeMap, bool debug = false );
     35                const Node * replace( const Node * node, const ExprMap & exprMap);
    3336        }
    3437}
  • src/AST/Expr.cpp

    r3c64c668 r58fe85a  
    2020#include <vector>
    2121
     22#include "Copy.hpp"                // for shallowCopy
     23#include "Eval.hpp"                // for call
    2224#include "GenericSubstitution.hpp"
     25#include "LinkageSpec.hpp"
    2326#include "Stmt.hpp"
    2427#include "Type.hpp"
     
    2730#include "Common/SemanticError.h"
    2831#include "GenPoly/Lvalue.h"        // for referencesPermissable
    29 #include "InitTweak/InitTweak.h"   // for getPointerBase
     32#include "InitTweak/InitTweak.h"   // for getFunction, getPointerBase
    3033#include "ResolvExpr/typeops.h"    // for extractResultType
    3134#include "Tuples/Tuples.h"         // for makeTupleType
    3235
    3336namespace ast {
     37
     38namespace {
     39        std::set<std::string> const lvalueFunctionNames = {"*?", "?[?]"};
     40}
     41
     42// --- Expr
     43bool Expr::get_lvalue() const {
     44        return false;
     45}
    3446
    3547// --- ApplicationExpr
     
    4658}
    4759
     60bool ApplicationExpr::get_lvalue() const {
     61        if ( const DeclWithType * func = InitTweak::getFunction( this ) ) {
     62                return func->linkage == Linkage::Intrinsic && lvalueFunctionNames.count( func->name );
     63        }
     64        return false;
     65}
     66
    4867// --- UntypedExpr
    4968
    50 UntypedExpr * UntypedExpr::createDeref( const CodeLocation & loc, Expr * arg ) {
     69UntypedExpr * UntypedExpr::createDeref( const CodeLocation & loc, const Expr * arg ) {
    5170        assert( arg );
    5271
    53         UntypedExpr * ret = new UntypedExpr{
    54                 loc, new NameExpr{loc, "*?"}, std::vector<ptr<Expr>>{ ptr<Expr>{ arg } }
    55         };
     72        UntypedExpr * ret = call( loc, "*?", arg );
    5673        if ( const Type * ty = arg->result ) {
    5774                const Type * base = InitTweak::getPointerBase( ty );
     
    6582                        // base type
    6683                        ret->result = base;
    67                         add_qualifiers( ret->result, CV::Lvalue );
    6884                }
    6985        }
     
    7187}
    7288
    73 UntypedExpr * UntypedExpr::createAssign( const CodeLocation & loc, Expr * lhs, Expr * rhs ) {
     89bool UntypedExpr::get_lvalue() const {
     90        std::string fname = InitTweak::getFunctionName( this );
     91        return lvalueFunctionNames.count( fname );
     92}
     93
     94UntypedExpr * UntypedExpr::createAssign( const CodeLocation & loc, const Expr * lhs, const Expr * rhs ) {
    7495        assert( lhs && rhs );
    7596
    76         UntypedExpr * ret = new UntypedExpr{
    77                 loc, new NameExpr{loc, "?=?"}, std::vector<ptr<Expr>>{ ptr<Expr>{ lhs }, ptr<Expr>{ rhs } }
    78         };
     97        UntypedExpr * ret = call( loc, "?=?", lhs, rhs );
    7998        if ( lhs->result && rhs->result ) {
    8099                // if both expressions are typed, assumes that this assignment is a C bitwise assignment,
     
    83102        }
    84103        return ret;
     104}
     105
     106// --- VariableExpr
     107
     108VariableExpr::VariableExpr( const CodeLocation & loc )
     109: Expr( loc ), var( nullptr ) {}
     110
     111VariableExpr::VariableExpr( const CodeLocation & loc, const DeclWithType * v )
     112: Expr( loc ), var( v ) {
     113        assert( var );
     114        assert( var->get_type() );
     115        result = shallowCopy( var->get_type() );
     116}
     117
     118bool VariableExpr::get_lvalue() const {
     119        // It isn't always an lvalue, but it is never an rvalue.
     120        return true;
     121}
     122
     123VariableExpr * VariableExpr::functionPointer(
     124                const CodeLocation & loc, const FunctionDecl * decl ) {
     125        // wrap usually-determined result type in a pointer
     126        VariableExpr * funcExpr = new VariableExpr{ loc, decl };
     127        funcExpr->result = new PointerType{ funcExpr->result };
     128        return funcExpr;
    85129}
    86130
     
    108152AddressExpr::AddressExpr( const CodeLocation & loc, const Expr * a ) : Expr( loc ), arg( a ) {
    109153        if ( arg->result ) {
    110                 if ( arg->result->is_lvalue() ) {
     154                if ( arg->get_lvalue() ) {
    111155                        // lvalue, retains all levels of reference, and gains a pointer inside the references
    112156                        Type * res = addrType( arg->result );
    113                         res->set_lvalue( false ); // result of & is never an lvalue
    114157                        result = res;
    115158                } else {
     
    118161                                        dynamic_cast< const ReferenceType * >( arg->result.get() ) ) {
    119162                                Type * res = addrType( refType->base );
    120                                 res->set_lvalue( false ); // result of & is never an lvalue
    121163                                result = res;
    122164                        } else {
     
    139181: Expr( loc, new VoidType{} ), arg( a ), isGenerated( g ) {}
    140182
     183bool CastExpr::get_lvalue() const {
     184        // This is actually wrong by C, but it works with our current set-up.
     185        return arg->get_lvalue();
     186}
     187
    141188// --- KeywordCastExpr
    142189
    143190const char * KeywordCastExpr::targetString() const {
    144191        return AggregateDecl::aggrString( target );
     192}
     193
     194// --- UntypedMemberExpr
     195
     196bool UntypedMemberExpr::get_lvalue() const {
     197        return aggregate->get_lvalue();
    145198}
    146199
     
    153206        assert( aggregate->result );
    154207
    155         // take ownership of member type
    156208        result = mem->get_type();
     209
    157210        // substitute aggregate generic parameters into member type
    158211        genericSubstitution( aggregate->result ).apply( result );
    159         // ensure lvalue and appropriate restrictions from aggregate type
    160         add_qualifiers( result, aggregate->result->qualifiers | CV::Lvalue );
    161 }
    162 
    163 // --- VariableExpr
    164 
    165 VariableExpr::VariableExpr( const CodeLocation & loc )
    166 : Expr( loc ), var( nullptr ) {}
    167 
    168 VariableExpr::VariableExpr( const CodeLocation & loc, const DeclWithType * v )
    169 : Expr( loc ), var( v ) {
    170         assert( var );
    171         assert( var->get_type() );
    172         result = var->get_type();
    173         add_qualifiers( result, CV::Lvalue );
    174 }
    175 
    176 VariableExpr * VariableExpr::functionPointer(
    177                 const CodeLocation & loc, const FunctionDecl * decl ) {
    178         // wrap usually-determined result type in a pointer
    179         VariableExpr * funcExpr = new VariableExpr{ loc, decl };
    180         funcExpr->result = new PointerType{ funcExpr->result };
    181         return funcExpr;
     212        // ensure appropriate restrictions from aggregate type
     213        add_qualifiers( result, aggregate->result->qualifiers );
     214}
     215
     216MemberExpr::MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg,
     217    MemberExpr::NoOpConstruction overloadSelector )
     218: Expr( loc ), member( mem ), aggregate( agg ) {
     219        assert( member );
     220        assert( aggregate );
     221        assert( aggregate->result );
     222        (void) overloadSelector;
     223}
     224
     225bool MemberExpr::get_lvalue() const {
     226        // This is actually wrong by C, but it works with our current set-up.
     227        return true;
    182228}
    183229
     
    258304: Expr( loc, new BasicType{ BasicType::SignedInt } ), arg1( a1 ), arg2( a2 ), isAnd( ia ) {}
    259305
     306// --- CommaExpr
     307bool CommaExpr::get_lvalue() const {
     308        // This is wrong by C, but the current implementation uses it.
     309        // (ex: Specialize, Lvalue and Box)
     310        return arg2->get_lvalue();
     311}
     312
    260313// --- ConstructorExpr
    261314
     
    276329        assert( t && i );
    277330        result = t;
    278         add_qualifiers( result, CV::Lvalue );
     331}
     332
     333bool CompoundLiteralExpr::get_lvalue() const {
     334        return true;
    279335}
    280336
     
    293349        // like MemberExpr, TupleIndexExpr is always an lvalue
    294350        result = type->types[ index ];
    295         add_qualifiers( result, CV::Lvalue );
     351}
     352
     353bool TupleIndexExpr::get_lvalue() const {
     354        return tuple->get_lvalue();
    296355}
    297356
  • src/AST/Expr.hpp

    r3c64c668 r58fe85a  
    3131
    3232// Must be included in *all* AST classes; should be #undef'd at the end of the file
    33 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
     33#define MUTATE_FRIEND \
     34    template<typename node_t> friend node_t * mutate(const node_t * node); \
     35        template<typename node_t> friend node_t * shallowCopy(const node_t * node);
     36
    3437
    3538class ConverterOldToNew;
     
    4245struct ParamEntry {
    4346        UniqueId decl;
    44         ptr<Decl> declptr;
     47        readonly<Decl> declptr;
    4548        ptr<Type> actualType;
    4649        ptr<Type> formalType;
     
    6265class Expr : public ParseNode {
    6366public:
    64         /// Saves space (~16 bytes) by combining ResnSlots and InferredParams
     67        /*
     68         * NOTE: the union approach is incorrect until the case of
     69         * partial resolution in InferMatcher is eliminated.
     70         * it is reverted to allow unresolved and resolved parameters
     71         * to coexist in an expression node.
     72         */
    6573        struct InferUnion {
     74                // mode is now unused
    6675                enum { Empty, Slots, Params } mode;
    67                 union data_t {
    68                         char def;
    69                         ResnSlots resnSlots;
    70                         InferredParams inferParams;
    71 
    72                         data_t() : def('\0') {}
    73                         ~data_t() {}
     76                struct data_t {
     77                        // char def;
     78                        ResnSlots * resnSlots;
     79                        InferredParams * inferParams;
     80
     81                        data_t(): resnSlots(nullptr), inferParams(nullptr) {}
     82                        data_t(const data_t &other) = delete;
     83                        ~data_t() {
     84                                delete resnSlots;
     85                                delete inferParams;
     86                        }
    7487                } data;
    7588
    7689                /// initializes from other InferUnion
    7790                void init_from( const InferUnion& o ) {
    78                         switch ( o.mode ) {
    79                         case Empty:  return;
    80                         case Slots:  new(&data.resnSlots) ResnSlots{ o.data.resnSlots }; return;
    81                         case Params: new(&data.inferParams) InferredParams{ o.data.inferParams }; return;
     91                        if (o.data.resnSlots) {
     92                                data.resnSlots = new ResnSlots(*o.data.resnSlots);
     93                        }
     94                        if (o.data.inferParams) {
     95                                data.inferParams = new InferredParams(*o.data.inferParams);
    8296                        }
    8397                }
     
    8599                /// initializes from other InferUnion (move semantics)
    86100                void init_from( InferUnion&& o ) {
    87                         switch ( o.mode ) {
    88                         case Empty:  return;
    89                         case Slots:  new(&data.resnSlots) ResnSlots{ std::move(o.data.resnSlots) }; return;
    90                         case Params:
    91                                 new(&data.inferParams) InferredParams{ std::move(o.data.inferParams) }; return;
    92                         }
    93                 }
    94 
    95                 /// clears variant fields
    96                 void reset() {
    97                         switch( mode ) {
    98                         case Empty:  return;
    99                         case Slots:  data.resnSlots.~ResnSlots(); return;
    100                         case Params: data.inferParams.~InferredParams(); return;
    101                         }
     101                        data.resnSlots = o.data.resnSlots;
     102                        data.inferParams = o.data.inferParams;
     103                        o.data.resnSlots = nullptr;
     104                        o.data.inferParams = nullptr;
    102105                }
    103106
     
    107110                InferUnion& operator= ( const InferUnion& ) = delete;
    108111                InferUnion& operator= ( InferUnion&& ) = delete;
    109                 ~InferUnion() { reset(); }
     112
     113                bool hasSlots() const { return data.resnSlots; }
     114                bool hasParams() const { return data.inferParams; }
    110115
    111116                ResnSlots& resnSlots() {
    112                         switch (mode) {
    113                         case Empty: new(&data.resnSlots) ResnSlots{}; mode = Slots; // fallthrough
    114                         case Slots: return data.resnSlots;
    115                         case Params: assertf(false, "Cannot return to resnSlots from Params"); abort();
     117                        if (!data.resnSlots) {
     118                                data.resnSlots = new ResnSlots();
    116119                        }
    117                         assertf(false, "unreachable");
     120                        return *data.resnSlots;
    118121                }
    119122
    120123                const ResnSlots& resnSlots() const {
    121                         if (mode == Slots) {
    122                                 return data.resnSlots;
     124                        if (data.resnSlots) {
     125                                return *data.resnSlots;
    123126                        }
    124127                        assertf(false, "Mode was not already resnSlots");
     
    127130
    128131                InferredParams& inferParams() {
    129                         switch (mode) {
    130                         case Slots: data.resnSlots.~ResnSlots(); // fallthrough
    131                         case Empty: new(&data.inferParams) InferredParams{}; mode = Params; // fallthrough
    132                         case Params: return data.inferParams;
     132                        if (!data.inferParams) {
     133                                data.inferParams = new InferredParams();
    133134                        }
    134                         assertf(false, "unreachable");
     135                        return *data.inferParams;
    135136                }
    136137
    137138                const InferredParams& inferParams() const {
    138                         if (mode == Params) {
    139                                 return data.inferParams;
     139                        if (data.inferParams) {
     140                                return *data.inferParams;
    140141                        }
    141142                        assertf(false, "Mode was not already Params");
     
    143144                }
    144145
    145                 void set_inferParams( InferredParams && ps ) {
    146                         switch(mode) {
    147                         case Slots:
    148                                 data.resnSlots.~ResnSlots();
    149                                 // fallthrough
    150                         case Empty:
    151                                 new(&data.inferParams) InferredParams{ std::move( ps ) };
    152                                 mode = Params;
    153                                 break;
    154                         case Params:
    155                                 data.inferParams = std::move( ps );
    156                                 break;
    157                         }
     146                void set_inferParams( InferredParams * ps ) {
     147                        delete data.resnSlots;
     148                        data.resnSlots = nullptr;
     149                        delete data.inferParams;
     150                        data.inferParams = ps;
    158151                }
    159152
     
    161154                /// and the other is in `Params`.
    162155                void splice( InferUnion && o ) {
    163                         if ( o.mode == Empty ) return;
    164                         if ( mode == Empty ) { init_from( o ); return; }
    165                         assert( mode == o.mode && "attempt to splice incompatible InferUnion" );
    166 
    167                         if ( mode == Slots ){
    168                                 data.resnSlots.insert(
    169                                         data.resnSlots.end(), o.data.resnSlots.begin(), o.data.resnSlots.end() );
    170                         } else if ( mode == Params ) {
    171                                 for ( const auto & p : o.data.inferParams ) {
    172                                         data.inferParams[p.first] = std::move(p.second);
     156                        if (o.data.resnSlots) {
     157                                if (data.resnSlots) {
     158                                        data.resnSlots->insert(
     159                                                data.resnSlots->end(), o.data.resnSlots->begin(), o.data.resnSlots->end() );
     160                                        delete o.data.resnSlots;
    173161                                }
    174                         } else assertf(false, "invalid mode");
     162                                else {
     163                                        data.resnSlots = o.data.resnSlots;
     164                                }
     165                                o.data.resnSlots = nullptr;
     166                        }
     167
     168                        if (o.data.inferParams) {
     169                                if (data.inferParams) {
     170                                        for ( const auto & p : *o.data.inferParams ) {
     171                                                (*data.inferParams)[p.first] = std::move(p.second);
     172                                        }
     173                                        delete o.data.inferParams;
     174                                }
     175                                else {
     176                                        data.inferParams = o.data.inferParams;
     177                                }
     178                                o.data.inferParams = nullptr;
     179                        }
    175180                }
    176181        };
     
    185190
    186191        Expr * set_extension( bool ex ) { extension = ex; return this; }
     192        virtual bool get_lvalue() const;
    187193
    188194        virtual const Expr * accept( Visitor & v ) const override = 0;
     
    201207        ApplicationExpr( const CodeLocation & loc, const Expr * f, std::vector<ptr<Expr>> && as = {} );
    202208
     209        bool get_lvalue() const final;
     210
    203211        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
    204212private:
     
    216224        : Expr( loc ), func( f ), args( std::move(as) ) {}
    217225
     226        bool get_lvalue() const final;
     227
    218228        /// Creates a new dereference expression
    219         static UntypedExpr * createDeref( const CodeLocation & loc, Expr * arg );
     229        static UntypedExpr * createDeref( const CodeLocation & loc, const Expr * arg );
    220230        /// Creates a new assignment expression
    221         static UntypedExpr * createAssign( const CodeLocation & loc, Expr * lhs, Expr * rhs );
     231        static UntypedExpr * createAssign( const CodeLocation & loc, const Expr * lhs, const Expr * rhs );
    222232
    223233        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
     
    241251};
    242252
     253/// A reference to a named variable.
     254class VariableExpr final : public Expr {
     255public:
     256        readonly<DeclWithType> var;
     257
     258        VariableExpr( const CodeLocation & loc );
     259        VariableExpr( const CodeLocation & loc, const DeclWithType * v );
     260
     261        bool get_lvalue() const final;
     262
     263        /// generates a function pointer for a given function
     264        static VariableExpr * functionPointer( const CodeLocation & loc, const FunctionDecl * decl );
     265
     266        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
     267private:
     268        VariableExpr * clone() const override { return new VariableExpr{ *this }; }
     269        MUTATE_FRIEND
     270};
     271
    243272/// Address-of expression `&e`
    244273class AddressExpr final : public Expr {
     
    271300};
    272301
    273 /// Whether a cast existed in the program source or not
     302/// Inidicates whether the cast is introduced by the CFA type system.
     303/// GeneratedCast for casts that the resolver introduces to force a return type
     304/// ExplicitCast for casts from user code
     305/// ExplicitCast for casts from desugaring advanced CFA features into simpler CFA
     306/// example
     307///   int * p;     // declaration
     308///   (float *) p; // use, with subject cast
     309/// subject cast being GeneratedCast means we are considering an interpretation with a type mismatch
     310/// subject cast being ExplicitCast means someone in charge wants it that way
    274311enum GeneratedFlag { ExplicitCast, GeneratedCast };
    275312
     
    291328        CastExpr( const Expr * a ) : CastExpr( a->location, a, GeneratedCast ) {}
    292329
     330        bool get_lvalue() const final;
     331
    293332        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
    294333private:
     
    301340public:
    302341        ptr<Expr> arg;
     342        struct Concrete {
     343                std::string field;
     344                std::string getter;
     345
     346                Concrete() = default;
     347                Concrete(const Concrete &) = default;
     348        };
    303349        ast::AggregateDecl::Aggregate target;
     350        Concrete concrete_target;
     351
    304352
    305353        KeywordCastExpr( const CodeLocation & loc, const Expr * a, ast::AggregateDecl::Aggregate t )
    306354        : Expr( loc ), arg( a ), target( t ) {}
    307355
     356        KeywordCastExpr( const CodeLocation & loc, const Expr * a, ast::AggregateDecl::Aggregate t, const Concrete & ct )
     357        : Expr( loc ), arg( a ), target( t ), concrete_target( ct ) {}
     358
    308359        /// Get a name for the target type
    309360        const char * targetString() const;
     
    338389        : Expr( loc ), member( mem ), aggregate( agg ) { assert( aggregate ); }
    339390
     391        bool get_lvalue() const final;
     392
    340393        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
    341394private:
     
    352405        MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg );
    353406
     407        bool get_lvalue() const final;
     408
    354409        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
    355410private:
    356411        MemberExpr * clone() const override { return new MemberExpr{ *this }; }
    357412        MUTATE_FRIEND
    358 };
    359 
    360 /// A reference to a named variable.
    361 class VariableExpr final : public Expr {
    362 public:
    363         readonly<DeclWithType> var;
    364 
    365         VariableExpr( const CodeLocation & loc );
    366         VariableExpr( const CodeLocation & loc, const DeclWithType * v );
    367 
    368         /// generates a function pointer for a given function
    369         static VariableExpr * functionPointer( const CodeLocation & loc, const FunctionDecl * decl );
    370 
    371         const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
    372 private:
    373         VariableExpr * clone() const override { return new VariableExpr{ *this }; }
    374         MUTATE_FRIEND
     413
     414        // Custructor overload meant only for AST conversion
     415        enum NoOpConstruction { NoOpConstructionChosen };
     416        MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg,
     417            NoOpConstruction overloadSelector );
     418        friend class ::ConverterOldToNew;
     419        friend class ::ConverterNewToOld;
    375420};
    376421
     
    386431                const CodeLocation & loc, const Type * ty, const std::string & r,
    387432                        std::optional<unsigned long long> i )
    388         : Expr( loc, ty ), rep( r ), ival( i ) {}
     433        : Expr( loc, ty ), rep( r ), ival( i ), underlyer(ty) {}
    389434
    390435        /// Gets the integer value of this constant, if one is appropriate to its type.
     
    532577
    533578        CommaExpr( const CodeLocation & loc, const Expr * a1, const Expr * a2 )
    534         : Expr( loc ), arg1( a1 ), arg2( a2 ) {}
     579        : Expr( loc ), arg1( a1 ), arg2( a2 ) {
     580                this->result = a2->result;
     581        }
     582
     583        bool get_lvalue() const final;
    535584
    536585        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
     
    577626
    578627        ImplicitCopyCtorExpr( const CodeLocation& loc, const ApplicationExpr * call )
    579         : Expr( loc, call->result ) { assert( call ); }
     628        : Expr( loc, call->result ), callExpr(call) { assert( call ); assert(call->result); }
    580629
    581630        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
     
    605654        CompoundLiteralExpr( const CodeLocation & loc, const Type * t, const Init * i );
    606655
     656        bool get_lvalue() const final;
     657
    607658        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
    608659private:
     
    660711
    661712        TupleIndexExpr( const CodeLocation & loc, const Expr * t, unsigned i );
     713
     714        bool get_lvalue() const final;
    662715
    663716        const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
     
    698751        std::vector<ptr<Expr>> dtors;              ///< destructor(s) for return variable(s)
    699752
     753        readonly<ExprStmt> resultExpr;
     754
    700755        StmtExpr( const CodeLocation & loc, const CompoundStmt * ss );
    701756
  • src/AST/Fwd.hpp

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May  8 16:05:00 2019
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Jun 24 09:48:00 2019
    13 // Update Count     : 1
     12// Last Modified On : Thr Jul 23 14:15:00 2020
     13// Update Count     : 2
    1414//
    1515
     
    5353class CatchStmt;
    5454class FinallyStmt;
     55class SuspendStmt;
    5556class WaitForStmt;
    5657class WithStmt;
     
    106107class QualifiedType;
    107108class FunctionType;
    108 class ReferenceToType;
    109 class StructInstType;
    110 class UnionInstType;
    111 class EnumInstType;
     109class BaseInstType;
     110template<typename decl_t> class SueInstType;
     111using StructInstType = SueInstType<StructDecl>;
     112using UnionInstType = SueInstType<UnionDecl>;
     113using EnumInstType = SueInstType<EnumDecl>;
    112114class TraitInstType;
    113115class TypeInstType;
     
    135137typedef unsigned int UniqueId;
    136138
     139struct TranslationUnit;
     140// TODO: Get from the TranslationUnit:
     141extern ptr<Type> sizeType;
     142extern const FunctionDecl * dereferenceOperator;
     143extern const StructDecl   * dtorStruct;
     144extern const FunctionDecl * dtorStructDestroy;
     145
    137146}
  • src/AST/GenericSubstitution.cpp

    r3c64c668 r58fe85a  
    4242        private:
    4343                // make substitution for generic type
    44                 void makeSub( const ReferenceToType * ty ) {
     44                void makeSub( const BaseInstType * ty ) {
    4545                        visit_children = false;
    4646                        const AggregateDecl * aggr = ty->aggr();
     
    6262        Pass<GenericSubstitutionBuilder> builder;
    6363        maybe_accept( ty, builder );
    64         return std::move(builder.pass.sub);
     64        return std::move(builder.core.sub);
    6565}
    6666
  • src/AST/Init.hpp

    r3c64c668 r58fe85a  
    2525
    2626// Must be included in *all* AST classes; should be #undef'd at the end of the file
    27 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
     27#define MUTATE_FRIEND \
     28    template<typename node_t> friend node_t * mutate(const node_t * node); \
     29        template<typename node_t> friend node_t * shallowCopy(const node_t * node);
    2830
    2931namespace ast {
     
    4850
    4951/// Flag for whether to construct from initialzier
    50 enum ConstructFlag { DoConstruct, MaybeConstruct };
     52enum ConstructFlag { NoConstruct, MaybeConstruct };
    5153
    5254/// Object initializer base class
     
    6971        ptr<Expr> value;
    7072
    71         SingleInit( const CodeLocation & loc, const Expr * val, ConstructFlag mc = DoConstruct )
     73        SingleInit( const CodeLocation & loc, const Expr * val, ConstructFlag mc = NoConstruct )
    7274        : Init( loc, mc ), value( val ) {}
    7375
     
    8890
    8991        ListInit( const CodeLocation & loc, std::vector<ptr<Init>> && is,
    90                 std::vector<ptr<Designation>> && ds = {}, ConstructFlag mc = DoConstruct );
     92                std::vector<ptr<Designation>> && ds = {}, ConstructFlag mc = NoConstruct );
    9193
    9294        using iterator = std::vector<ptr<Init>>::iterator;
     
    116118        ConstructorInit(
    117119                const CodeLocation & loc, const Stmt * ctor, const Stmt * dtor, const Init * init )
    118         : Init( loc, DoConstruct ), ctor( ctor ), dtor( dtor ), init( init ) {}
     120        : Init( loc, MaybeConstruct ), ctor( ctor ), dtor( dtor ), init( init ) {}
    119121
    120122        const Init * accept( Visitor & v ) const override { return v.visit( this ); }
  • src/AST/Node.cpp

    r3c64c668 r58fe85a  
    99// Author           : Thierry Delisle
    1010// Created On       : Thu May 16 14:16:00 2019
    11 // Last Modified By :
    12 // Last Modified On :
    13 // Update Count     :
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Fri Jun  5 10:21:00 2020
     13// Update Count     : 1
    1414//
    1515
     
    1717#include "Fwd.hpp"
    1818
     19#include <csignal>  // MEMORY DEBUG -- for raise
    1920#include <iostream>
    2021
     
    2930#include "Print.hpp"
    3031
    31 template< typename node_t, enum ast::Node::ref_type ref_t >
    32 void ast::ptr_base<node_t, ref_t>::_inc( const node_t * node ) { node->increment(ref_t); }
    33 
    34 template< typename node_t, enum ast::Node::ref_type ref_t >
    35 void ast::ptr_base<node_t, ref_t>::_dec( const node_t * node ) { node->decrement(ref_t); }
    36 
    37 template< typename node_t, enum ast::Node::ref_type ref_t >
    38 void ast::ptr_base<node_t, ref_t>::_check() const { if(node) assert(node->was_ever_strong == false || node->strong_count > 0); }
     32/// MEMORY DEBUG -- allows breaking on ref-count changes of dynamically chosen object.
     33/// Process to use in GDB:
     34///   break ast::Node::_trap()
     35///   run
     36///   set variable MEM_TRAP_OBJ = <target>
     37///   disable <first breakpoint>
     38///   continue
     39void * MEM_TRAP_OBJ = nullptr;
     40
     41void _trap( const void * node ) {
     42        if ( node == MEM_TRAP_OBJ ) std::raise(SIGTRAP);
     43}
     44
     45[[noreturn]] static inline void strict_fail(const ast::Node * node) {
     46        assertf(node, "strict_as had nullptr input.");
     47        const ast::ParseNode * parse = dynamic_cast<const ast::ParseNode *>( node );
     48        if ( nullptr == parse ) {
     49                assertf(nullptr, "%s (no location)", toString(node).c_str());
     50        } else if ( parse->location.isUnset() ) {
     51                assertf(nullptr, "%s (unset location)", toString(node).c_str());
     52        } else {
     53                assertf(nullptr, "%s (at %s:%d)", toString(node).c_str(),
     54                        parse->location.filename.c_str(), parse->location.first_line);
     55        }
     56}
     57
     58template< typename node_t, enum ast::Node::ref_type ref_t >
     59void ast::ptr_base<node_t, ref_t>::_strict_fail() const {
     60        strict_fail(node);
     61}
     62
     63template< typename node_t, enum ast::Node::ref_type ref_t >
     64void ast::ptr_base<node_t, ref_t>::_inc( const node_t * node ) {
     65        node->increment(ref_t);
     66        _trap( node );
     67}
     68
     69template< typename node_t, enum ast::Node::ref_type ref_t >
     70void ast::ptr_base<node_t, ref_t>::_dec( const node_t * node, bool do_delete ) {
     71        _trap( node );
     72        node->decrement( ref_t, do_delete );
     73}
     74
     75template< typename node_t, enum ast::Node::ref_type ref_t >
     76void ast::ptr_base<node_t, ref_t>::_check() const {
     77        // if(node) assert(node->was_ever_strong == false || node->strong_count > 0);
     78}
    3979
    4080template< typename node_t, enum ast::Node::ref_type ref_t >
     
    226266template class ast::ptr_base< ast::FunctionType, ast::Node::ref_type::weak >;
    227267template class ast::ptr_base< ast::FunctionType, ast::Node::ref_type::strong >;
    228 template class ast::ptr_base< ast::ReferenceToType, ast::Node::ref_type::weak >;
    229 template class ast::ptr_base< ast::ReferenceToType, ast::Node::ref_type::strong >;
     268template class ast::ptr_base< ast::BaseInstType, ast::Node::ref_type::weak >;
     269template class ast::ptr_base< ast::BaseInstType, ast::Node::ref_type::strong >;
    230270template class ast::ptr_base< ast::StructInstType, ast::Node::ref_type::weak >;
    231271template class ast::ptr_base< ast::StructInstType, ast::Node::ref_type::strong >;
  • src/AST/Node.hpp

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May 8 10:27:04 2019
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Jun  3 13:26:00 2019
    13 // Update Count     : 5
     12// Last Modified On : Fri Jun 5 9:47:00 2020
     13// Update Count     : 6
    1414//
    1515
     
    3838        Node& operator= (const Node&) = delete;
    3939        Node& operator= (Node&&) = delete;
    40         virtual ~Node() = default;
     40        virtual ~Node() {}
    4141
    4242        virtual const Node * accept( Visitor & v ) const = 0;
     
    4949
    5050        bool unique() const { return strong_count == 1; }
     51        bool isManaged() const {return strong_count > 0; }
    5152
    5253private:
     
    5758        template<typename node_t>
    5859        friend node_t * mutate(const node_t * node);
     60        template<typename node_t>
     61        friend node_t * shallowCopy(const node_t * node);
    5962
    6063        mutable size_t strong_count = 0;
     
    6972        }
    7073
    71         void decrement(ast::Node::ref_type ref) const {
     74        void decrement(ast::Node::ref_type ref, bool do_delete = true) const {
    7275                switch (ref) {
    7376                        case ref_type::strong: strong_count--; break;
     
    7578                }
    7679
    77                 if(!strong_count && !weak_count) {
     80                if( do_delete && !strong_count && !weak_count) {
    7881                        delete this;
    7982                }
     
    9497        assertf(
    9598                node->weak_count == 0,
    96                 "Error: mutating node with weak references to it will invalided some references"
     99                "Error: mutating node with weak references to it will invalidate some references"
    97100        );
    98101        return node->clone();
     
    104107        // skip mutate if equivalent
    105108        if ( node->*field == val ) return node;
    106        
     109
    107110        // mutate and return
    108111        node_t * ret = mutate( node );
     
    123126        (ret->*field)[i] = std::forward< field_t >( val );
    124127        return ret;
     128}
     129
     130/// Mutate an entire indexed collection by cloning to accepted value
     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 ) {
     133        for ( unsigned i = 0; i < (node->*field).size(); ++i ) {
     134                node = mutate_field_index( node, field, i, (node->*field)[i]->accept( v ) );
     135        }
     136        return node;
    125137}
    126138
     
    217229        const node_t & operator* () const { _check(); return *node; }
    218230        explicit operator bool() const { _check(); return node; }
    219         operator const node_t * () const { _check(); return node; }
     231        operator const node_t * () const & { _check(); return node; }
     232        operator const node_t * () && = delete;
     233
     234        const node_t * release() {
     235                const node_t * ret = node;
     236                if ( node ) {
     237                        _dec(node, false);
     238                        node = nullptr;
     239                }
     240                return ret;
     241        }
    220242
    221243        /// wrapper for convenient access to dynamic_cast
     
    223245        const o_node_t * as() const { _check(); return dynamic_cast<const o_node_t *>(node); }
    224246
    225         /// wrapper for convenient access to strict_dynamic_cast
     247        /// Wrapper that makes sure dynamic_cast returns non-null.
    226248        template<typename o_node_t>
    227         const o_node_t * strict_as() const { _check(); return strict_dynamic_cast<const o_node_t *>(node); }
     249        const o_node_t * strict_as() const {
     250                if (const o_node_t * ret = as<o_node_t>()) return ret;
     251                _strict_fail();
     252        }
     253
     254        /// Wrapper that makes sure dynamic_cast does not fail.
     255        template<typename o_node_t, decltype(nullptr) null>
     256        const o_node_t * strict_as() const { return node ? strict_as<o_node_t>() : nullptr; }
    228257
    229258        /// Returns a mutable version of the pointer in this node.
     
    244273
    245274        void _inc( const node_t * other );
    246         void _dec( const node_t * other );
     275        void _dec( const node_t * other, bool do_delete = true );
    247276        void _check() const;
     277        void _strict_fail() const __attribute__((noreturn));
    248278
    249279        const node_t * node;
  • src/AST/Pass.hpp

    r3c64c668 r58fe85a  
    88//
    99// Author           : Thierry Delisle
    10 // Created On       : Thu May 09 15::37::05 2019
     10// Created On       : Thu May 09 15:37:05 2019
    1111// Last Modified By :
    1212// Last Modified On :
     
    4646//
    4747// Several additional features are available through inheritance
    48 // | WithTypeSubstitution - provides polymorphic const TypeSubstitution * env for the
    49 //                          current expression
    50 // | WithStmtsToAdd       - provides the ability to insert statements before or after the current
    51 //                          statement by adding new statements into stmtsToAddBefore or
    52 //                          stmtsToAddAfter respectively.
    53 // | WithDeclsToAdd       - provides the ability to insert declarations before or after the current
    54 //                          declarations by adding new DeclStmt into declsToAddBefore or
    55 //                          declsToAddAfter respectively.
    56 // | WithShortCircuiting  - provides the ability to skip visiting child nodes; set visit_children
    57 //                          to false in pre{visit,visit} to skip visiting children
    58 // | WithGuards           - provides the ability to save/restore data like a LIFO stack; to save,
    59 //                          call GuardValue with the variable to save, the variable will
    60 //                          automatically be restored to its previous value after the corresponding
    61 //                          postvisit/postmutate teminates.
    62 // | WithVisitorRef       - provides an pointer to the templated visitor wrapper
    63 // | WithSymbolTable      - provides symbol table functionality
     48// | PureVisitor           - makes the visitor pure, it never modifies nodes in place and always
     49//                           clones nodes it needs to make changes to
     50// | WithConstTypeSubstitution - provides polymorphic const TypeSubstitution * typeSubs for the
     51//                           current expression
     52// | WithStmtsToAdd        - provides the ability to insert statements before or after the current
     53//                           statement by adding new statements into stmtsToAddBefore or
     54//                           stmtsToAddAfter respectively.
     55// | WithDeclsToAdd        - provides the ability to insert declarations before or after the
     56//                           current declarations by adding new DeclStmt into declsToAddBefore or
     57//                           declsToAddAfter respectively.
     58// | WithShortCircuiting   - provides the ability to skip visiting child nodes; set visit_children
     59//                           to false in pre{visit,visit} to skip visiting children
     60// | WithGuards            - provides the ability to save/restore data like a LIFO stack; to save,
     61//                           call GuardValue with the variable to save, the variable will
     62//                           automatically be restored to its previous value after the
     63//                           corresponding postvisit/postmutate teminates.
     64// | WithVisitorRef        - provides an pointer to the templated visitor wrapper
     65// | WithSymbolTable       - provides symbol table functionality
     66//
     67// Other Special Members:
     68// | result                - Either a method that takes no parameters or a field. If a method (or
     69//                           callable field) get_result calls it, otherwise the value is returned.
    6470//-------------------------------------------------------------------------------------------------
    65 template< typename pass_t >
     71template< typename core_t >
    6672class Pass final : public ast::Visitor {
    6773public:
     74        using core_type = core_t;
     75        using type = Pass<core_t>;
     76
    6877        /// Forward any arguments to the pass constructor
    6978        /// Propagate 'this' if necessary
    7079        template< typename... Args >
    7180        Pass( Args &&... args)
    72                 : pass( std::forward<Args>( args )... )
     81                : core( std::forward<Args>( args )... )
    7382        {
    7483                // After the pass is constructed, check if it wants the have a pointer to the wrapping visitor
    75                 typedef Pass<pass_t> this_t;
    76                 this_t * const * visitor = __pass::visitor(pass, 0);
     84                type * const * visitor = __pass::visitor(core, 0);
    7785                if(visitor) {
    78                         *const_cast<this_t **>( visitor ) = this;
     86                        *const_cast<type **>( visitor ) = this;
    7987                }
    8088        }
     
    8290        virtual ~Pass() = default;
    8391
    84         /// Storage for the actual pass
    85         pass_t pass;
     92        /// Storage for the actual pass.
     93        core_t core;
     94
     95        /// If the core defines a result, call it if possible, otherwise return it.
     96        inline auto get_result() -> decltype( __pass::get_result( core, '0' ) ) {
     97                return __pass::get_result( core, '0' );
     98        }
     99
     100        /// Construct and run a pass on a translation unit.
     101        template< typename... Args >
     102        static void run( TranslationUnit & decls, Args &&... args ) {
     103                Pass<core_t> visitor( std::forward<Args>( args )... );
     104                accept_all( decls, visitor );
     105        }
     106
     107        /// Contruct and run a pass on a pointer to extract a value.
     108        template< typename node_type, typename... Args >
     109        static auto read( node_type const * node, Args&&... args ) {
     110                Pass<core_t> visitor( std::forward<Args>( args )... );
     111                node_type const * temp = node->accept( visitor );
     112                assert( temp == node );
     113                return visitor.get_result();
     114        }
     115
     116        // Versions of the above for older compilers.
     117        template< typename... Args >
     118        static void run( TranslationUnit & decls ) {
     119                Pass<core_t> visitor;
     120                accept_all( decls, visitor );
     121        }
     122
     123        template< typename node_type, typename... Args >
     124        static auto read( node_type const * node ) {
     125                Pass<core_t> visitor;
     126                node_type const * temp = node->accept( visitor );
     127                assert( temp == node );
     128                return visitor.get_result();
     129        }
    86130
    87131        /// Visit function declarations
     
    111155        const ast::Stmt *             visit( const ast::CatchStmt            * ) override final;
    112156        const ast::Stmt *             visit( const ast::FinallyStmt          * ) override final;
     157        const ast::Stmt *             visit( const ast::SuspendStmt          * ) override final;
    113158        const ast::Stmt *             visit( const ast::WaitForStmt          * ) override final;
    114159        const ast::Decl *             visit( const ast::WithStmt             * ) override final;
     
    178223        const ast::TypeSubstitution * visit( const ast::TypeSubstitution     * ) override final;
    179224
    180         template<typename pass_type>
    181         friend void accept_all( std::list< ptr<Decl> > & decls, Pass<pass_type>& visitor );
     225        template<typename core_type>
     226        friend void accept_all( std::list< ptr<Decl> > & decls, Pass<core_type>& visitor );
     227
     228        bool isInFunction() const {
     229                return inFunction;
     230        }
     231
    182232private:
    183233
    184         bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children(pass, 0); return ptr ? *ptr : true; }
     234        bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children(core, 0); return ptr ? *ptr : true; }
    185235
    186236private:
    187237        const ast::Stmt * call_accept( const ast::Stmt * );
    188238        const ast::Expr * call_accept( const ast::Expr * );
     239
     240        // requests WithStmtsToAdd directly add to this statement, as if it is a compound.
     241
     242        const ast::Stmt * call_accept_as_compound(const ast::Stmt *);
    189243
    190244        template< typename node_t >
     
    206260        void maybe_accept(const node_t * &, child_t parent_t::* child);
    207261
     262        template<typename node_t, typename parent_t, typename child_t>
     263        void maybe_accept_as_compound(const node_t * &, child_t parent_t::* child);
     264
    208265private:
    209266        /// Internal RAII guard for symbol table features
    210267        struct guard_symtab {
    211                 guard_symtab( Pass<pass_t> & pass ): pass( pass ) { __pass::symtab::enter(pass, 0); }
    212                 ~guard_symtab()                                   { __pass::symtab::leave(pass, 0); }
    213                 Pass<pass_t> & pass;
     268                guard_symtab( Pass<core_t> & pass ): pass( pass ) { __pass::symtab::enter(pass.core, 0); }
     269                ~guard_symtab()                                   { __pass::symtab::leave(pass.core, 0); }
     270                Pass<core_t> & pass;
    214271        };
    215272
    216273        /// Internal RAII guard for scope features
    217274        struct guard_scope {
    218                 guard_scope( Pass<pass_t> & pass ): pass( pass ) { __pass::scope::enter(pass, 0); }
    219                 ~guard_scope()                                   { __pass::scope::leave(pass, 0); }
    220                 Pass<pass_t> & pass;
     275                guard_scope( Pass<core_t> & pass ): pass( pass ) { __pass::scope::enter(pass.core, 0); }
     276                ~guard_scope()                                   { __pass::scope::leave(pass.core, 0); }
     277                Pass<core_t> & pass;
     278        };
     279
     280        /// Internal RAII guard for forall substitutions
     281        struct guard_forall_subs {
     282                guard_forall_subs( Pass<core_t> & pass, const FunctionType * type )
     283                : pass( pass ), type( type ) { __pass::forall::enter(pass.core, 0, type ); }
     284                ~guard_forall_subs()         { __pass::forall::leave(pass.core, 0, type ); }
     285                Pass<core_t> & pass;
     286                const FunctionType * type;
    221287        };
    222288
    223289private:
    224290        bool inFunction = false;
     291        bool atFunctionTop = false;
    225292};
    226293
    227294/// Apply a pass to an entire translation unit
    228 template<typename pass_t>
    229 void accept_all( std::list< ast::ptr<ast::Decl> > &, ast::Pass<pass_t> & visitor );
     295template<typename core_t>
     296void accept_all( std::list< ast::ptr<ast::Decl> > &, ast::Pass<core_t> & visitor );
     297
     298template<typename core_t>
     299void accept_all( ast::TranslationUnit &, ast::Pass<core_t> & visitor );
    230300
    231301//-------------------------------------------------------------------------------------------------
     
    233303//-------------------------------------------------------------------------------------------------
    234304
    235 /// Keep track of the polymorphic const TypeSubstitution * env for the current expression
     305/// If used the visitor will always clone nodes.
     306struct PureVisitor {};
     307
     308/// Keep track of the polymorphic const TypeSubstitution * typeSubs for the current expression.
    236309struct WithConstTypeSubstitution {
    237         const TypeSubstitution * env = nullptr;
     310        const TypeSubstitution * typeSubs = nullptr;
    238311};
    239312
     
    267340        };
    268341
    269         template< typename pass_t>
    270         friend auto __pass::at_cleanup( pass_t & pass, int ) -> decltype( &pass.at_cleanup );
     342        template< typename core_t>
     343        friend auto __pass::at_cleanup( core_t & core, int ) -> decltype( &core.at_cleanup );
    271344public:
    272345
     
    304377
    305378/// Used to get a pointer to the pass with its wrapped type
    306 template<typename pass_t>
     379template<typename core_t>
    307380struct WithVisitorRef {
    308         Pass<pass_t> * const visitor = nullptr;
     381        Pass<core_t> * const visitor = nullptr;
     382
     383        bool isInFunction() const {
     384                return visitor->isInFunction();
     385        }
    309386};
    310387
     
    313390        SymbolTable symtab;
    314391};
     392
    315393}
    316394
     
    320398extern struct PassVisitorStats {
    321399        size_t depth = 0;
    322         Stats::Counters::MaxCounter<double> * max = nullptr;
    323         Stats::Counters::AverageCounter<double> * avg = nullptr;
     400        Stats::Counters::MaxCounter<double> * max;
     401        Stats::Counters::AverageCounter<double> * avg;
    324402} pass_visitor_stats;
    325403}
  • src/AST/Pass.impl.hpp

    r3c64c668 r58fe85a  
    2020#include <unordered_map>
    2121
     22#include "AST/TranslationUnit.hpp"
    2223#include "AST/TypeSubstitution.hpp"
    2324
     
    2526        using namespace ast; \
    2627        /* back-up the visit children */ \
    27         __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children(pass, 0) ); \
     28        __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children(core, 0) ); \
    2829        /* setup the scope for passes that want to run code at exit */ \
    29         __attribute__((unused)) ast::__pass::guard_value          guard2( ast::__pass::at_cleanup    (pass, 0) ); \
     30        __attribute__((unused)) ast::__pass::guard_value          guard2( ast::__pass::at_cleanup    (core, 0) ); \
     31        /* begin tracing memory allocation if requested by this pass */ \
     32        __pass::beginTrace( core, 0 ); \
    3033        /* call the implementation of the previsit of this pass */ \
    31         __pass::previsit( pass, node, 0 );
     34        __pass::previsit( core, node, 0 );
    3235
    3336#define VISIT( code... ) \
     
    4043#define VISIT_END( type, node ) \
    4144        /* call the implementation of the postvisit of this pass */ \
    42         auto __return = __pass::postvisit( pass, node, 0 ); \
     45        auto __return = __pass::postvisit( core, node, 0 ); \
    4346        assertf(__return, "post visit should never return null"); \
     47        /* end tracing memory allocation if requested by this pass */ \
     48        __pass::endTrace( core, 0 ); \
    4449        return __return;
    4550
     
    5358
    5459namespace ast {
     60        template<typename node_t>
     61        node_t * shallowCopy( const node_t * node );
     62
    5563        namespace __pass {
    5664                // Check if this is either a null pointer or a pointer to an empty container
     
    6068                }
    6169
     70                template< typename core_t, typename node_t >
     71                static inline node_t* mutate(const node_t *node) {
     72                        return std::is_base_of<PureVisitor, core_t>::value ? ::ast::shallowCopy(node) : ::ast::mutate(node);
     73                }
     74
    6275                //------------------------------
    6376                template<typename it_t, template <class...> class container_t>
     
    119132        }
    120133
    121         template< typename pass_t >
     134        template< typename core_t >
    122135        template< typename node_t >
    123         auto ast::Pass< pass_t >::call_accept( const node_t * node )
     136        auto ast::Pass< core_t >::call_accept( const node_t * node )
    124137                -> typename std::enable_if<
    125138                                !std::is_base_of<ast::Expr, node_t>::value &&
     
    127140                        , decltype( node->accept(*this) )
    128141                >::type
    129 
    130142        {
    131143                __pedantic_pass_assert( __visit_children() );
    132                 __pedantic_pass_assert( expr );
     144                __pedantic_pass_assert( node );
    133145
    134146                static_assert( !std::is_base_of<ast::Expr, node_t>::value, "ERROR");
     
    138150        }
    139151
    140         template< typename pass_t >
    141         const ast::Expr * ast::Pass< pass_t >::call_accept( const ast::Expr * expr ) {
     152        template< typename core_t >
     153        const ast::Expr * ast::Pass< core_t >::call_accept( const ast::Expr * expr ) {
    142154                __pedantic_pass_assert( __visit_children() );
    143155                __pedantic_pass_assert( expr );
    144156
    145                 const ast::TypeSubstitution ** env_ptr = __pass::env( pass, 0);
    146                 if ( env_ptr && expr->env ) {
    147                         *env_ptr = expr->env;
     157                const ast::TypeSubstitution ** typeSubs_ptr = __pass::typeSubs( core, 0 );
     158                if ( typeSubs_ptr && expr->env ) {
     159                        *typeSubs_ptr = expr->env;
    148160                }
    149161
     
    151163        }
    152164
    153         template< typename pass_t >
    154         const ast::Stmt * ast::Pass< pass_t >::call_accept( const ast::Stmt * stmt ) {
     165        template< typename core_t >
     166        const ast::Stmt * ast::Pass< core_t >::call_accept( const ast::Stmt * stmt ) {
    155167                __pedantic_pass_assert( __visit_children() );
    156168                __pedantic_pass_assert( stmt );
    157169
     170                return stmt->accept( *this );
     171        }
     172
     173        template< typename core_t >
     174        const ast::Stmt * ast::Pass< core_t >::call_accept_as_compound( const ast::Stmt * stmt ) {
     175                __pedantic_pass_assert( __visit_children() );
     176                __pedantic_pass_assert( stmt );
     177
    158178                // add a few useful symbols to the scope
    159179                using __pass::empty;
    160180
    161181                // get the stmts/decls that will need to be spliced in
    162                 auto stmts_before = __pass::stmtsToAddBefore( pass, 0);
    163                 auto stmts_after  = __pass::stmtsToAddAfter ( pass, 0);
    164                 auto decls_before = __pass::declsToAddBefore( pass, 0);
    165                 auto decls_after  = __pass::declsToAddAfter ( pass, 0);
     182                auto stmts_before = __pass::stmtsToAddBefore( core, 0);
     183                auto stmts_after  = __pass::stmtsToAddAfter ( core, 0);
     184                auto decls_before = __pass::declsToAddBefore( core, 0);
     185                auto decls_after  = __pass::declsToAddAfter ( core, 0);
    166186
    167187                // These may be modified by subnode but most be restored once we exit this statemnet.
    168                 ValueGuardPtr< const ast::TypeSubstitution * > __old_env         ( __pass::env( pass, 0) );
     188                ValueGuardPtr< const ast::TypeSubstitution * > __old_env         ( __pass::typeSubs( core, 0 ) );
    169189                ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) >::type > __old_decls_before( stmts_before );
    170190                ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) >::type > __old_decls_after ( stmts_after  );
     
    202222        }
    203223
    204         template< typename pass_t >
     224        template< typename core_t >
    205225        template< template <class...> class container_t >
    206         container_t< ptr<Stmt> > ast::Pass< pass_t >::call_accept( const container_t< ptr<Stmt> > & statements ) {
     226        container_t< ptr<Stmt> > ast::Pass< core_t >::call_accept( const container_t< ptr<Stmt> > & statements ) {
    207227                __pedantic_pass_assert( __visit_children() );
    208228                if( statements.empty() ) return {};
     
    215235
    216236                // get the stmts/decls that will need to be spliced in
    217                 auto stmts_before = __pass::stmtsToAddBefore( pass, 0);
    218                 auto stmts_after  = __pass::stmtsToAddAfter ( pass, 0);
    219                 auto decls_before = __pass::declsToAddBefore( pass, 0);
    220                 auto decls_after  = __pass::declsToAddAfter ( pass, 0);
     237                auto stmts_before = __pass::stmtsToAddBefore( core, 0);
     238                auto stmts_after  = __pass::stmtsToAddAfter ( core, 0);
     239                auto decls_before = __pass::declsToAddBefore( core, 0);
     240                auto decls_after  = __pass::declsToAddAfter ( core, 0);
    221241
    222242                // These may be modified by subnode but most be restored once we exit this statemnet.
     
    268288        }
    269289
    270         template< typename pass_t >
     290        template< typename core_t >
    271291        template< template <class...> class container_t, typename node_t >
    272         container_t< ast::ptr<node_t> > ast::Pass< pass_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) {
     292        container_t< ast::ptr<node_t> > ast::Pass< core_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) {
    273293                __pedantic_pass_assert( __visit_children() );
    274294                if( container.empty() ) return {};
     
    299319        }
    300320
    301         template< typename pass_t >
     321        template< typename core_t >
    302322        template<typename node_t, typename parent_t, typename child_t>
    303         void ast::Pass< pass_t >::maybe_accept(
     323        void ast::Pass< core_t >::maybe_accept(
    304324                const node_t * & parent,
    305325                child_t parent_t::*child
     
    317337
    318338                if( __pass::differs(old_val, new_val) ) {
    319                         auto new_parent = mutate(parent);
     339                        auto new_parent = __pass::mutate<core_t>(parent);
     340                        new_parent->*child = new_val;
     341                        parent = new_parent;
     342                }
     343        }
     344
     345        template< typename core_t >
     346        template<typename node_t, typename parent_t, typename child_t>
     347        void ast::Pass< core_t >::maybe_accept_as_compound(
     348                const node_t * & parent,
     349                child_t parent_t::*child
     350        ) {
     351                static_assert( std::is_base_of<parent_t, node_t>::value, "Error deducing member object" );
     352
     353                if(__pass::skip(parent->*child)) return;
     354                const auto & old_val = __pass::get(parent->*child, 0);
     355
     356                static_assert( !std::is_same<const ast::Node * &, decltype(old_val)>::value, "ERROR");
     357
     358                auto new_val = call_accept_as_compound( old_val );
     359
     360                static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value || std::is_same<int, decltype(old_val)>::value, "ERROR");
     361
     362                if( __pass::differs(old_val, new_val) ) {
     363                        auto new_parent = __pass::mutate<core_t>(parent);
    320364                        new_parent->*child = new_val;
    321365                        parent = new_parent;
     
    333377//------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    334378
    335 template< typename pass_t >
    336 inline void ast::accept_all( std::list< ast::ptr<ast::Decl> > & decls, ast::Pass< pass_t > & visitor ) {
     379template< typename core_t >
     380inline void ast::accept_all( std::list< ast::ptr<ast::Decl> > & decls, ast::Pass< core_t > & visitor ) {
    337381        // We are going to aggregate errors for all these statements
    338382        SemanticErrorException errors;
     
    342386
    343387        // get the stmts/decls that will need to be spliced in
    344         auto decls_before = __pass::declsToAddBefore( visitor.pass, 0);
    345         auto decls_after  = __pass::declsToAddAfter ( visitor.pass, 0);
     388        auto decls_before = __pass::declsToAddBefore( visitor.core, 0);
     389        auto decls_after  = __pass::declsToAddAfter ( visitor.core, 0);
    346390
    347391        // update pass statitistics
     
    363407                }
    364408                catch( SemanticErrorException &e ) {
    365                         errors.append( e );
     409                        if (__pass::on_error (visitor.core, *i, 0))
     410                                errors.append( e );
    366411                }
    367412
     
    371416        pass_visitor_stats.depth--;
    372417        if ( !errors.isEmpty() ) { throw errors; }
     418}
     419
     420template< typename core_t >
     421inline void ast::accept_all( ast::TranslationUnit & unit, ast::Pass< core_t > & visitor ) {
     422        return ast::accept_all( unit.decls, visitor );
    373423}
    374424
     
    392442//--------------------------------------------------------------------------
    393443// ObjectDecl
    394 template< typename pass_t >
    395 const ast::DeclWithType * ast::Pass< pass_t >::visit( const ast::ObjectDecl * node ) {
     444template< typename core_t >
     445const ast::DeclWithType * ast::Pass< core_t >::visit( const ast::ObjectDecl * node ) {
    396446        VISIT_START( node );
    397447
     
    406456        )
    407457
    408         __pass::symtab::addId( pass, 0, node );
     458        __pass::symtab::addId( core, 0, node );
    409459
    410460        VISIT_END( DeclWithType, node );
     
    413463//--------------------------------------------------------------------------
    414464// FunctionDecl
    415 template< typename pass_t >
    416 const ast::DeclWithType * ast::Pass< pass_t >::visit( const ast::FunctionDecl * node ) {
    417         VISIT_START( node );
    418 
    419         __pass::symtab::addId( pass, 0, node );
     465template< typename core_t >
     466const ast::DeclWithType * ast::Pass< core_t >::visit( const ast::FunctionDecl * node ) {
     467        VISIT_START( node );
     468
     469        __pass::symtab::addId( core, 0, node );
    420470
    421471        VISIT(maybe_accept( node, &FunctionDecl::withExprs );)
     
    425475                // shadow with exprs and not the other way around.
    426476                guard_symtab guard { *this };
    427                 __pass::symtab::addWith( pass, 0, node->withExprs, node );
     477                __pass::symtab::addWith( core, 0, node->withExprs, node );
    428478                {
    429479                        guard_symtab guard { *this };
    430480                        // implicit add __func__ identifier as specified in the C manual 6.4.2.2
    431                         static ast::ObjectDecl func(
    432                                 node->location, "__func__",
    433                                 new ast::ArrayType(
    434                                         new ast::BasicType( ast::BasicType::Char, ast::CV::Qualifiers( ast::CV::Const ) ),
     481                        static ast::ptr< ast::ObjectDecl > func{ new ast::ObjectDecl{
     482                                CodeLocation{}, "__func__",
     483                                new ast::ArrayType{
     484                                        new ast::BasicType{ ast::BasicType::Char, ast::CV::Const },
    435485                                        nullptr, VariableLen, DynamicDim
    436                                 )
    437                         );
    438                         __pass::symtab::addId( pass, 0, &func );
     486                                }
     487                        } };
     488                        __pass::symtab::addId( core, 0, func );
    439489                        VISIT(
    440                                 maybe_accept( node, &FunctionDecl::type );
    441                                 // function body needs to have the same scope as parameters - CompoundStmt will not enter
    442                                 // a new scope if inFunction is true
     490                                // parameter declarations
     491                                maybe_accept( node, &FunctionDecl::params );
     492                                maybe_accept( node, &FunctionDecl::returns );
     493                                // type params and assertions
     494                                maybe_accept( node, &FunctionDecl::type_params );
     495                                maybe_accept( node, &FunctionDecl::assertions );
     496                                // First remember that we are now within a function.
    443497                                ValueGuard< bool > oldInFunction( inFunction );
    444498                                inFunction = true;
     499                                // The function body needs to have the same scope as parameters.
     500                                // A CompoundStmt will not enter a new scope if atFunctionTop is true.
     501                                ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
     502                                atFunctionTop = true;
    445503                                maybe_accept( node, &FunctionDecl::stmts );
    446504                                maybe_accept( node, &FunctionDecl::attributes );
     
    454512//--------------------------------------------------------------------------
    455513// StructDecl
    456 template< typename pass_t >
    457 const ast::Decl * ast::Pass< pass_t >::visit( const ast::StructDecl * node ) {
     514template< typename core_t >
     515const ast::Decl * ast::Pass< core_t >::visit( const ast::StructDecl * node ) {
    458516        VISIT_START( node );
    459517
    460518        // make up a forward declaration and add it before processing the members
    461519        // needs to be on the heap because addStruct saves the pointer
    462         __pass::symtab::addStructFwd( pass, 0, node );
     520        __pass::symtab::addStructFwd( core, 0, node );
    463521
    464522        VISIT({
     
    469527
    470528        // this addition replaces the forward declaration
    471         __pass::symtab::addStruct( pass, 0, node );
     529        __pass::symtab::addStruct( core, 0, node );
    472530
    473531        VISIT_END( Decl, node );
     
    476534//--------------------------------------------------------------------------
    477535// UnionDecl
    478 template< typename pass_t >
    479 const ast::Decl * ast::Pass< pass_t >::visit( const ast::UnionDecl * node ) {
     536template< typename core_t >
     537const ast::Decl * ast::Pass< core_t >::visit( const ast::UnionDecl * node ) {
    480538        VISIT_START( node );
    481539
    482540        // make up a forward declaration and add it before processing the members
    483         __pass::symtab::addUnionFwd( pass, 0, node );
     541        __pass::symtab::addUnionFwd( core, 0, node );
    484542
    485543        VISIT({
     
    489547        })
    490548
    491         __pass::symtab::addUnion( pass, 0, node );
     549        __pass::symtab::addUnion( core, 0, node );
    492550
    493551        VISIT_END( Decl, node );
     
    496554//--------------------------------------------------------------------------
    497555// EnumDecl
    498 template< typename pass_t >
    499 const ast::Decl * ast::Pass< pass_t >::visit( const ast::EnumDecl * node ) {
    500         VISIT_START( node );
    501 
    502         __pass::symtab::addEnum( pass, 0, node );
     556template< typename core_t >
     557const ast::Decl * ast::Pass< core_t >::visit( const ast::EnumDecl * node ) {
     558        VISIT_START( node );
     559
     560        __pass::symtab::addEnum( core, 0, node );
    503561
    504562        VISIT(
     
    513571//--------------------------------------------------------------------------
    514572// TraitDecl
    515 template< typename pass_t >
    516 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TraitDecl * node ) {
     573template< typename core_t >
     574const ast::Decl * ast::Pass< core_t >::visit( const ast::TraitDecl * node ) {
    517575        VISIT_START( node );
    518576
     
    523581        })
    524582
    525         __pass::symtab::addTrait( pass, 0, node );
     583        __pass::symtab::addTrait( core, 0, node );
    526584
    527585        VISIT_END( Decl, node );
     
    530588//--------------------------------------------------------------------------
    531589// TypeDecl
    532 template< typename pass_t >
    533 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TypeDecl * node ) {
     590template< typename core_t >
     591const ast::Decl * ast::Pass< core_t >::visit( const ast::TypeDecl * node ) {
    534592        VISIT_START( node );
    535593
    536594        VISIT({
    537595                guard_symtab guard { *this };
    538                 maybe_accept( node, &TypeDecl::params );
    539596                maybe_accept( node, &TypeDecl::base   );
    540597        })
     
    543600        // note that assertions come after the type is added to the symtab, since they are not part of the type proper
    544601        // and may depend on the type itself
    545         __pass::symtab::addType( pass, 0, node );
     602        __pass::symtab::addType( core, 0, node );
    546603
    547604        VISIT(
     
    559616//--------------------------------------------------------------------------
    560617// TypedefDecl
    561 template< typename pass_t >
    562 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TypedefDecl * node ) {
     618template< typename core_t >
     619const ast::Decl * ast::Pass< core_t >::visit( const ast::TypedefDecl * node ) {
    563620        VISIT_START( node );
    564621
    565622        VISIT({
    566623                guard_symtab guard { *this };
    567                 maybe_accept( node, &TypedefDecl::params );
    568624                maybe_accept( node, &TypedefDecl::base   );
    569625        })
    570626
    571         __pass::symtab::addType( pass, 0, node );
     627        __pass::symtab::addType( core, 0, node );
    572628
    573629        VISIT( maybe_accept( node, &TypedefDecl::assertions ); )
     
    578634//--------------------------------------------------------------------------
    579635// AsmDecl
    580 template< typename pass_t >
    581 const ast::AsmDecl * ast::Pass< pass_t >::visit( const ast::AsmDecl * node ) {
     636template< typename core_t >
     637const ast::AsmDecl * ast::Pass< core_t >::visit( const ast::AsmDecl * node ) {
    582638        VISIT_START( node );
    583639
     
    591647//--------------------------------------------------------------------------
    592648// StaticAssertDecl
    593 template< typename pass_t >
    594 const ast::StaticAssertDecl * ast::Pass< pass_t >::visit( const ast::StaticAssertDecl * node ) {
     649template< typename core_t >
     650const ast::StaticAssertDecl * ast::Pass< core_t >::visit( const ast::StaticAssertDecl * node ) {
    595651        VISIT_START( node );
    596652
     
    605661//--------------------------------------------------------------------------
    606662// CompoundStmt
    607 template< typename pass_t >
    608 const ast::CompoundStmt * ast::Pass< pass_t >::visit( const ast::CompoundStmt * node ) {
    609         VISIT_START( node );
    610         VISIT({
    611                 // do not enter a new scope if inFunction is true - needs to check old state before the assignment
    612                 auto guard1 = makeFuncGuard( [this, inFunction = this->inFunction]() {
    613                         if ( ! inFunction ) __pass::symtab::enter(pass, 0);
    614                 }, [this, inFunction = this->inFunction]() {
    615                         if ( ! inFunction ) __pass::symtab::leave(pass, 0);
     663template< typename core_t >
     664const ast::CompoundStmt * ast::Pass< core_t >::visit( const ast::CompoundStmt * node ) {
     665        VISIT_START( node );
     666        VISIT(
     667                // Do not enter (or leave) a new scope if atFunctionTop. Remember to save the result.
     668                auto guard1 = makeFuncGuard( [this, enterScope = !this->atFunctionTop]() {
     669                        if ( enterScope ) {
     670                                __pass::symtab::enter(core, 0);
     671                                __pass::scope::enter(core, 0);
     672                        }
     673                }, [this, leaveScope = !this->atFunctionTop]() {
     674                        if ( leaveScope ) {
     675                                __pass::symtab::leave(core, 0);
     676                                __pass::scope::leave(core, 0);
     677                        }
    616678                });
    617                 ValueGuard< bool > guard2( inFunction );
     679                ValueGuard< bool > guard2( atFunctionTop );
     680                atFunctionTop = false;
    618681                guard_scope guard3 { *this };
    619                 inFunction = false;
    620682                maybe_accept( node, &CompoundStmt::kids );
    621         })
     683        )
    622684        VISIT_END( CompoundStmt, node );
    623685}
     
    625687//--------------------------------------------------------------------------
    626688// ExprStmt
    627 template< typename pass_t >
    628 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ExprStmt * node ) {
     689template< typename core_t >
     690const ast::Stmt * ast::Pass< core_t >::visit( const ast::ExprStmt * node ) {
    629691        VISIT_START( node );
    630692
     
    638700//--------------------------------------------------------------------------
    639701// AsmStmt
    640 template< typename pass_t >
    641 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::AsmStmt * node ) {
     702template< typename core_t >
     703const ast::Stmt * ast::Pass< core_t >::visit( const ast::AsmStmt * node ) {
    642704        VISIT_START( node )
    643705
     
    654716//--------------------------------------------------------------------------
    655717// DirectiveStmt
    656 template< typename pass_t >
    657 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::DirectiveStmt * node ) {
     718template< typename core_t >
     719const ast::Stmt * ast::Pass< core_t >::visit( const ast::DirectiveStmt * node ) {
    658720        VISIT_START( node )
    659721
     
    663725//--------------------------------------------------------------------------
    664726// IfStmt
    665 template< typename pass_t >
    666 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::IfStmt * node ) {
     727template< typename core_t >
     728const ast::Stmt * ast::Pass< core_t >::visit( const ast::IfStmt * node ) {
    667729        VISIT_START( node );
    668730
     
    672734                maybe_accept( node, &IfStmt::inits    );
    673735                maybe_accept( node, &IfStmt::cond     );
    674                 maybe_accept( node, &IfStmt::thenPart );
    675                 maybe_accept( node, &IfStmt::elsePart );
     736                maybe_accept_as_compound( node, &IfStmt::thenPart );
     737                maybe_accept_as_compound( node, &IfStmt::elsePart );
    676738        })
    677739
     
    681743//--------------------------------------------------------------------------
    682744// WhileStmt
    683 template< typename pass_t >
    684 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::WhileStmt * node ) {
     745template< typename core_t >
     746const ast::Stmt * ast::Pass< core_t >::visit( const ast::WhileStmt * node ) {
    685747        VISIT_START( node );
    686748
     
    690752                maybe_accept( node, &WhileStmt::inits );
    691753                maybe_accept( node, &WhileStmt::cond  );
    692                 maybe_accept( node, &WhileStmt::body  );
     754                maybe_accept_as_compound( node, &WhileStmt::body  );
    693755        })
    694756
     
    698760//--------------------------------------------------------------------------
    699761// ForStmt
    700 template< typename pass_t >
    701 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ForStmt * node ) {
     762template< typename core_t >
     763const ast::Stmt * ast::Pass< core_t >::visit( const ast::ForStmt * node ) {
    702764        VISIT_START( node );
    703765
     
    705767                // for statements introduce a level of scope (for the initialization)
    706768                guard_symtab guard { *this };
     769                // xxx - old ast does not create WithStmtsToAdd scope for loop inits. should revisit this later.
    707770                maybe_accept( node, &ForStmt::inits );
    708771                maybe_accept( node, &ForStmt::cond  );
    709772                maybe_accept( node, &ForStmt::inc   );
    710                 maybe_accept( node, &ForStmt::body  );
     773                maybe_accept_as_compound( node, &ForStmt::body  );
    711774        })
    712775
     
    716779//--------------------------------------------------------------------------
    717780// SwitchStmt
    718 template< typename pass_t >
    719 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::SwitchStmt * node ) {
     781template< typename core_t >
     782const ast::Stmt * ast::Pass< core_t >::visit( const ast::SwitchStmt * node ) {
    720783        VISIT_START( node );
    721784
     
    730793//--------------------------------------------------------------------------
    731794// CaseStmt
    732 template< typename pass_t >
    733 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::CaseStmt * node ) {
     795template< typename core_t >
     796const ast::Stmt * ast::Pass< core_t >::visit( const ast::CaseStmt * node ) {
    734797        VISIT_START( node );
    735798
     
    744807//--------------------------------------------------------------------------
    745808// BranchStmt
    746 template< typename pass_t >
    747 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::BranchStmt * node ) {
     809template< typename core_t >
     810const ast::Stmt * ast::Pass< core_t >::visit( const ast::BranchStmt * node ) {
    748811        VISIT_START( node );
    749812        VISIT_END( Stmt, node );
     
    752815//--------------------------------------------------------------------------
    753816// ReturnStmt
    754 template< typename pass_t >
    755 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ReturnStmt * node ) {
     817template< typename core_t >
     818const ast::Stmt * ast::Pass< core_t >::visit( const ast::ReturnStmt * node ) {
    756819        VISIT_START( node );
    757820
     
    765828//--------------------------------------------------------------------------
    766829// ThrowStmt
    767 template< typename pass_t >
    768 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ThrowStmt * node ) {
     830template< typename core_t >
     831const ast::Stmt * ast::Pass< core_t >::visit( const ast::ThrowStmt * node ) {
    769832        VISIT_START( node );
    770833
     
    779842//--------------------------------------------------------------------------
    780843// TryStmt
    781 template< typename pass_t >
    782 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::TryStmt * node ) {
     844template< typename core_t >
     845const ast::Stmt * ast::Pass< core_t >::visit( const ast::TryStmt * node ) {
    783846        VISIT_START( node );
    784847
     
    794857//--------------------------------------------------------------------------
    795858// CatchStmt
    796 template< typename pass_t >
    797 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::CatchStmt * node ) {
     859template< typename core_t >
     860const ast::Stmt * ast::Pass< core_t >::visit( const ast::CatchStmt * node ) {
    798861        VISIT_START( node );
    799862
     
    803866                maybe_accept( node, &CatchStmt::decl );
    804867                maybe_accept( node, &CatchStmt::cond );
    805                 maybe_accept( node, &CatchStmt::body );
     868                maybe_accept_as_compound( node, &CatchStmt::body );
    806869        })
    807870
     
    811874//--------------------------------------------------------------------------
    812875// FinallyStmt
    813 template< typename pass_t >
    814 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::FinallyStmt * node ) {
     876template< typename core_t >
     877const ast::Stmt * ast::Pass< core_t >::visit( const ast::FinallyStmt * node ) {
    815878        VISIT_START( node );
    816879
     
    823886
    824887//--------------------------------------------------------------------------
     888// FinallyStmt
     889template< typename core_t >
     890const ast::Stmt * ast::Pass< core_t >::visit( const ast::SuspendStmt * node ) {
     891        VISIT_START( node );
     892
     893        VISIT(
     894                maybe_accept( node, &SuspendStmt::then   );
     895        )
     896
     897        VISIT_END( Stmt, node );
     898}
     899
     900//--------------------------------------------------------------------------
    825901// WaitForStmt
    826 template< typename pass_t >
    827 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::WaitForStmt * node ) {
     902template< typename core_t >
     903const ast::Stmt * ast::Pass< core_t >::visit( const ast::WaitForStmt * node ) {
    828904        VISIT_START( node );
    829905                // for( auto & clause : node->clauses ) {
     
    862938
    863939                if(mutated) {
    864                         auto n = mutate(node);
     940                        auto n = __pass::mutate<core_t>(node);
    865941                        n->clauses = std::move( new_clauses );
    866942                        node = n;
     
    872948                        auto nval = call_accept( node->field ); \
    873949                        if(nval != node->field ) { \
    874                                 auto nparent = mutate(node); \
     950                                auto nparent = __pass::mutate<core_t>(node); \
    875951                                nparent->field = nval; \
    876952                                node = nparent; \
     
    893969//--------------------------------------------------------------------------
    894970// WithStmt
    895 template< typename pass_t >
    896 const ast::Decl * ast::Pass< pass_t >::visit( const ast::WithStmt * node ) {
     971template< typename core_t >
     972const ast::Decl * ast::Pass< core_t >::visit( const ast::WithStmt * node ) {
    897973        VISIT_START( node );
    898974
     
    902978                        // catch statements introduce a level of scope (for the caught exception)
    903979                        guard_symtab guard { *this };
    904                         __pass::symtab::addWith( pass, 0, node->exprs, node );
     980                        __pass::symtab::addWith( core, 0, node->exprs, node );
    905981                        maybe_accept( node, &WithStmt::stmt );
    906982                }
     
    911987//--------------------------------------------------------------------------
    912988// NullStmt
    913 template< typename pass_t >
    914 const ast::NullStmt * ast::Pass< pass_t >::visit( const ast::NullStmt * node ) {
     989template< typename core_t >
     990const ast::NullStmt * ast::Pass< core_t >::visit( const ast::NullStmt * node ) {
    915991        VISIT_START( node );
    916992        VISIT_END( NullStmt, node );
     
    919995//--------------------------------------------------------------------------
    920996// DeclStmt
    921 template< typename pass_t >
    922 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::DeclStmt * node ) {
     997template< typename core_t >
     998const ast::Stmt * ast::Pass< core_t >::visit( const ast::DeclStmt * node ) {
    923999        VISIT_START( node );
    9241000
     
    9321008//--------------------------------------------------------------------------
    9331009// ImplicitCtorDtorStmt
    934 template< typename pass_t >
    935 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ImplicitCtorDtorStmt * node ) {
     1010template< typename core_t >
     1011const ast::Stmt * ast::Pass< core_t >::visit( const ast::ImplicitCtorDtorStmt * node ) {
    9361012        VISIT_START( node );
    9371013
    9381014        // For now this isn't visited, it is unclear if this causes problem
    9391015        // if all tests are known to pass, remove this code
    940         // VISIT(
    941         //      maybe_accept( node, &ImplicitCtorDtorStmt::callStmt );
    942         // )
     1016        VISIT(
     1017                maybe_accept( node, &ImplicitCtorDtorStmt::callStmt );
     1018        )
    9431019
    9441020        VISIT_END( Stmt, node );
     
    9471023//--------------------------------------------------------------------------
    9481024// ApplicationExpr
    949 template< typename pass_t >
    950 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ApplicationExpr * node ) {
     1025template< typename core_t >
     1026const ast::Expr * ast::Pass< core_t >::visit( const ast::ApplicationExpr * node ) {
    9511027        VISIT_START( node );
    9521028
     
    9651041//--------------------------------------------------------------------------
    9661042// UntypedExpr
    967 template< typename pass_t >
    968 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedExpr * node ) {
     1043template< typename core_t >
     1044const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedExpr * node ) {
    9691045        VISIT_START( node );
    9701046
     
    9831059//--------------------------------------------------------------------------
    9841060// NameExpr
    985 template< typename pass_t >
    986 const ast::Expr * ast::Pass< pass_t >::visit( const ast::NameExpr * node ) {
     1061template< typename core_t >
     1062const ast::Expr * ast::Pass< core_t >::visit( const ast::NameExpr * node ) {
    9871063        VISIT_START( node );
    9881064
     
    9971073//--------------------------------------------------------------------------
    9981074// CastExpr
    999 template< typename pass_t >
    1000 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CastExpr * node ) {
     1075template< typename core_t >
     1076const ast::Expr * ast::Pass< core_t >::visit( const ast::CastExpr * node ) {
    10011077        VISIT_START( node );
    10021078
     
    10131089//--------------------------------------------------------------------------
    10141090// KeywordCastExpr
    1015 template< typename pass_t >
    1016 const ast::Expr * ast::Pass< pass_t >::visit( const ast::KeywordCastExpr * node ) {
     1091template< typename core_t >
     1092const ast::Expr * ast::Pass< core_t >::visit( const ast::KeywordCastExpr * node ) {
    10171093        VISIT_START( node );
    10181094
     
    10291105//--------------------------------------------------------------------------
    10301106// VirtualCastExpr
    1031 template< typename pass_t >
    1032 const ast::Expr * ast::Pass< pass_t >::visit( const ast::VirtualCastExpr * node ) {
     1107template< typename core_t >
     1108const ast::Expr * ast::Pass< core_t >::visit( const ast::VirtualCastExpr * node ) {
    10331109        VISIT_START( node );
    10341110
     
    10451121//--------------------------------------------------------------------------
    10461122// AddressExpr
    1047 template< typename pass_t >
    1048 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AddressExpr * node ) {
     1123template< typename core_t >
     1124const ast::Expr * ast::Pass< core_t >::visit( const ast::AddressExpr * node ) {
    10491125        VISIT_START( node );
    10501126
     
    10611137//--------------------------------------------------------------------------
    10621138// LabelAddressExpr
    1063 template< typename pass_t >
    1064 const ast::Expr * ast::Pass< pass_t >::visit( const ast::LabelAddressExpr * node ) {
     1139template< typename core_t >
     1140const ast::Expr * ast::Pass< core_t >::visit( const ast::LabelAddressExpr * node ) {
    10651141        VISIT_START( node );
    10661142
     
    10751151//--------------------------------------------------------------------------
    10761152// UntypedMemberExpr
    1077 template< typename pass_t >
    1078 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedMemberExpr * node ) {
     1153template< typename core_t >
     1154const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedMemberExpr * node ) {
    10791155        VISIT_START( node );
    10801156
     
    10921168//--------------------------------------------------------------------------
    10931169// MemberExpr
    1094 template< typename pass_t >
    1095 const ast::Expr * ast::Pass< pass_t >::visit( const ast::MemberExpr * node ) {
     1170template< typename core_t >
     1171const ast::Expr * ast::Pass< core_t >::visit( const ast::MemberExpr * node ) {
    10961172        VISIT_START( node );
    10971173
     
    11081184//--------------------------------------------------------------------------
    11091185// VariableExpr
    1110 template< typename pass_t >
    1111 const ast::Expr * ast::Pass< pass_t >::visit( const ast::VariableExpr * node ) {
     1186template< typename core_t >
     1187const ast::Expr * ast::Pass< core_t >::visit( const ast::VariableExpr * node ) {
    11121188        VISIT_START( node );
    11131189
     
    11221198//--------------------------------------------------------------------------
    11231199// ConstantExpr
    1124 template< typename pass_t >
    1125 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConstantExpr * node ) {
     1200template< typename core_t >
     1201const ast::Expr * ast::Pass< core_t >::visit( const ast::ConstantExpr * node ) {
    11261202        VISIT_START( node );
    11271203
     
    11361212//--------------------------------------------------------------------------
    11371213// SizeofExpr
    1138 template< typename pass_t >
    1139 const ast::Expr * ast::Pass< pass_t >::visit( const ast::SizeofExpr * node ) {
     1214template< typename core_t >
     1215const ast::Expr * ast::Pass< core_t >::visit( const ast::SizeofExpr * node ) {
    11401216        VISIT_START( node );
    11411217
     
    11561232//--------------------------------------------------------------------------
    11571233// AlignofExpr
    1158 template< typename pass_t >
    1159 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AlignofExpr * node ) {
     1234template< typename core_t >
     1235const ast::Expr * ast::Pass< core_t >::visit( const ast::AlignofExpr * node ) {
    11601236        VISIT_START( node );
    11611237
     
    11761252//--------------------------------------------------------------------------
    11771253// UntypedOffsetofExpr
    1178 template< typename pass_t >
    1179 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedOffsetofExpr * node ) {
     1254template< typename core_t >
     1255const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedOffsetofExpr * node ) {
    11801256        VISIT_START( node );
    11811257
     
    11921268//--------------------------------------------------------------------------
    11931269// OffsetofExpr
    1194 template< typename pass_t >
    1195 const ast::Expr * ast::Pass< pass_t >::visit( const ast::OffsetofExpr * node ) {
     1270template< typename core_t >
     1271const ast::Expr * ast::Pass< core_t >::visit( const ast::OffsetofExpr * node ) {
    11961272        VISIT_START( node );
    11971273
     
    12081284//--------------------------------------------------------------------------
    12091285// OffsetPackExpr
    1210 template< typename pass_t >
    1211 const ast::Expr * ast::Pass< pass_t >::visit( const ast::OffsetPackExpr * node ) {
     1286template< typename core_t >
     1287const ast::Expr * ast::Pass< core_t >::visit( const ast::OffsetPackExpr * node ) {
    12121288        VISIT_START( node );
    12131289
     
    12241300//--------------------------------------------------------------------------
    12251301// LogicalExpr
    1226 template< typename pass_t >
    1227 const ast::Expr * ast::Pass< pass_t >::visit( const ast::LogicalExpr * node ) {
     1302template< typename core_t >
     1303const ast::Expr * ast::Pass< core_t >::visit( const ast::LogicalExpr * node ) {
    12281304        VISIT_START( node );
    12291305
     
    12411317//--------------------------------------------------------------------------
    12421318// ConditionalExpr
    1243 template< typename pass_t >
    1244 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConditionalExpr * node ) {
     1319template< typename core_t >
     1320const ast::Expr * ast::Pass< core_t >::visit( const ast::ConditionalExpr * node ) {
    12451321        VISIT_START( node );
    12461322
     
    12591335//--------------------------------------------------------------------------
    12601336// CommaExpr
    1261 template< typename pass_t >
    1262 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CommaExpr * node ) {
     1337template< typename core_t >
     1338const ast::Expr * ast::Pass< core_t >::visit( const ast::CommaExpr * node ) {
    12631339        VISIT_START( node );
    12641340
     
    12761352//--------------------------------------------------------------------------
    12771353// TypeExpr
    1278 template< typename pass_t >
    1279 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TypeExpr * node ) {
     1354template< typename core_t >
     1355const ast::Expr * ast::Pass< core_t >::visit( const ast::TypeExpr * node ) {
    12801356        VISIT_START( node );
    12811357
     
    12921368//--------------------------------------------------------------------------
    12931369// AsmExpr
    1294 template< typename pass_t >
    1295 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AsmExpr * node ) {
     1370template< typename core_t >
     1371const ast::Expr * ast::Pass< core_t >::visit( const ast::AsmExpr * node ) {
    12961372        VISIT_START( node );
    12971373
     
    13091385//--------------------------------------------------------------------------
    13101386// ImplicitCopyCtorExpr
    1311 template< typename pass_t >
    1312 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ImplicitCopyCtorExpr * node ) {
     1387template< typename core_t >
     1388const ast::Expr * ast::Pass< core_t >::visit( const ast::ImplicitCopyCtorExpr * node ) {
    13131389        VISIT_START( node );
    13141390
     
    13251401//--------------------------------------------------------------------------
    13261402// ConstructorExpr
    1327 template< typename pass_t >
    1328 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConstructorExpr * node ) {
     1403template< typename core_t >
     1404const ast::Expr * ast::Pass< core_t >::visit( const ast::ConstructorExpr * node ) {
    13291405        VISIT_START( node );
    13301406
     
    13411417//--------------------------------------------------------------------------
    13421418// CompoundLiteralExpr
    1343 template< typename pass_t >
    1344 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CompoundLiteralExpr * node ) {
     1419template< typename core_t >
     1420const ast::Expr * ast::Pass< core_t >::visit( const ast::CompoundLiteralExpr * node ) {
    13451421        VISIT_START( node );
    13461422
     
    13571433//--------------------------------------------------------------------------
    13581434// RangeExpr
    1359 template< typename pass_t >
    1360 const ast::Expr * ast::Pass< pass_t >::visit( const ast::RangeExpr * node ) {
     1435template< typename core_t >
     1436const ast::Expr * ast::Pass< core_t >::visit( const ast::RangeExpr * node ) {
    13611437        VISIT_START( node );
    13621438
     
    13741450//--------------------------------------------------------------------------
    13751451// UntypedTupleExpr
    1376 template< typename pass_t >
    1377 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedTupleExpr * node ) {
     1452template< typename core_t >
     1453const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedTupleExpr * node ) {
    13781454        VISIT_START( node );
    13791455
     
    13901466//--------------------------------------------------------------------------
    13911467// TupleExpr
    1392 template< typename pass_t >
    1393 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleExpr * node ) {
     1468template< typename core_t >
     1469const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleExpr * node ) {
    13941470        VISIT_START( node );
    13951471
     
    14061482//--------------------------------------------------------------------------
    14071483// TupleIndexExpr
    1408 template< typename pass_t >
    1409 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleIndexExpr * node ) {
     1484template< typename core_t >
     1485const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleIndexExpr * node ) {
    14101486        VISIT_START( node );
    14111487
     
    14221498//--------------------------------------------------------------------------
    14231499// TupleAssignExpr
    1424 template< typename pass_t >
    1425 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleAssignExpr * node ) {
     1500template< typename core_t >
     1501const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleAssignExpr * node ) {
    14261502        VISIT_START( node );
    14271503
     
    14381514//--------------------------------------------------------------------------
    14391515// StmtExpr
    1440 template< typename pass_t >
    1441 const ast::Expr * ast::Pass< pass_t >::visit( const ast::StmtExpr * node ) {
     1516template< typename core_t >
     1517const ast::Expr * ast::Pass< core_t >::visit( const ast::StmtExpr * node ) {
    14421518        VISIT_START( node );
    14431519
    14441520        VISIT(// don't want statements from outer CompoundStmts to be added to this StmtExpr
    14451521                // get the stmts that will need to be spliced in
    1446                 auto stmts_before = __pass::stmtsToAddBefore( pass, 0);
    1447                 auto stmts_after  = __pass::stmtsToAddAfter ( pass, 0);
     1522                auto stmts_before = __pass::stmtsToAddBefore( core, 0);
     1523                auto stmts_after  = __pass::stmtsToAddAfter ( core, 0);
    14481524
    14491525                // These may be modified by subnode but most be restored once we exit this statemnet.
    1450                 ValueGuardPtr< const ast::TypeSubstitution * > __old_env( __pass::env( pass, 0) );
     1526                ValueGuardPtr< const ast::TypeSubstitution * > __old_env( __pass::typeSubs( core, 0 ) );
    14511527                ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) >::type > __old_decls_before( stmts_before );
    14521528                ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) >::type > __old_decls_after ( stmts_after  );
     
    14661542//--------------------------------------------------------------------------
    14671543// UniqueExpr
    1468 template< typename pass_t >
    1469 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UniqueExpr * node ) {
     1544template< typename core_t >
     1545const ast::Expr * ast::Pass< core_t >::visit( const ast::UniqueExpr * node ) {
    14701546        VISIT_START( node );
    14711547
     
    14821558//--------------------------------------------------------------------------
    14831559// UntypedInitExpr
    1484 template< typename pass_t >
    1485 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedInitExpr * node ) {
     1560template< typename core_t >
     1561const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedInitExpr * node ) {
    14861562        VISIT_START( node );
    14871563
     
    14991575//--------------------------------------------------------------------------
    15001576// InitExpr
    1501 template< typename pass_t >
    1502 const ast::Expr * ast::Pass< pass_t >::visit( const ast::InitExpr * node ) {
     1577template< typename core_t >
     1578const ast::Expr * ast::Pass< core_t >::visit( const ast::InitExpr * node ) {
    15031579        VISIT_START( node );
    15041580
     
    15161592//--------------------------------------------------------------------------
    15171593// DeletedExpr
    1518 template< typename pass_t >
    1519 const ast::Expr * ast::Pass< pass_t >::visit( const ast::DeletedExpr * node ) {
     1594template< typename core_t >
     1595const ast::Expr * ast::Pass< core_t >::visit( const ast::DeletedExpr * node ) {
    15201596        VISIT_START( node );
    15211597
     
    15331609//--------------------------------------------------------------------------
    15341610// DefaultArgExpr
    1535 template< typename pass_t >
    1536 const ast::Expr * ast::Pass< pass_t >::visit( const ast::DefaultArgExpr * node ) {
     1611template< typename core_t >
     1612const ast::Expr * ast::Pass< core_t >::visit( const ast::DefaultArgExpr * node ) {
    15371613        VISIT_START( node );
    15381614
     
    15491625//--------------------------------------------------------------------------
    15501626// GenericExpr
    1551 template< typename pass_t >
    1552 const ast::Expr * ast::Pass< pass_t >::visit( const ast::GenericExpr * node ) {
     1627template< typename core_t >
     1628const ast::Expr * ast::Pass< core_t >::visit( const ast::GenericExpr * node ) {
    15531629        VISIT_START( node );
    15541630
     
    15781654
    15791655                if(mutated) {
    1580                         auto n = mutate(node);
     1656                        auto n = __pass::mutate<core_t>(node);
    15811657                        n->associations = std::move( new_kids );
    15821658                        node = n;
     
    15891665//--------------------------------------------------------------------------
    15901666// VoidType
    1591 template< typename pass_t >
    1592 const ast::Type * ast::Pass< pass_t >::visit( const ast::VoidType * node ) {
     1667template< typename core_t >
     1668const ast::Type * ast::Pass< core_t >::visit( const ast::VoidType * node ) {
    15931669        VISIT_START( node );
    15941670
     
    15981674//--------------------------------------------------------------------------
    15991675// BasicType
    1600 template< typename pass_t >
    1601 const ast::Type * ast::Pass< pass_t >::visit( const ast::BasicType * node ) {
     1676template< typename core_t >
     1677const ast::Type * ast::Pass< core_t >::visit( const ast::BasicType * node ) {
    16021678        VISIT_START( node );
    16031679
     
    16071683//--------------------------------------------------------------------------
    16081684// PointerType
    1609 template< typename pass_t >
    1610 const ast::Type * ast::Pass< pass_t >::visit( const ast::PointerType * node ) {
     1685template< typename core_t >
     1686const ast::Type * ast::Pass< core_t >::visit( const ast::PointerType * node ) {
    16111687        VISIT_START( node );
    16121688
     
    16211697//--------------------------------------------------------------------------
    16221698// ArrayType
    1623 template< typename pass_t >
    1624 const ast::Type * ast::Pass< pass_t >::visit( const ast::ArrayType * node ) {
     1699template< typename core_t >
     1700const ast::Type * ast::Pass< core_t >::visit( const ast::ArrayType * node ) {
    16251701        VISIT_START( node );
    16261702
     
    16351711//--------------------------------------------------------------------------
    16361712// ReferenceType
    1637 template< typename pass_t >
    1638 const ast::Type * ast::Pass< pass_t >::visit( const ast::ReferenceType * node ) {
     1713template< typename core_t >
     1714const ast::Type * ast::Pass< core_t >::visit( const ast::ReferenceType * node ) {
    16391715        VISIT_START( node );
    16401716
     
    16481724//--------------------------------------------------------------------------
    16491725// QualifiedType
    1650 template< typename pass_t >
    1651 const ast::Type * ast::Pass< pass_t >::visit( const ast::QualifiedType * node ) {
     1726template< typename core_t >
     1727const ast::Type * ast::Pass< core_t >::visit( const ast::QualifiedType * node ) {
    16521728        VISIT_START( node );
    16531729
     
    16621738//--------------------------------------------------------------------------
    16631739// FunctionType
    1664 template< typename pass_t >
    1665 const ast::Type * ast::Pass< pass_t >::visit( const ast::FunctionType * node ) {
    1666         VISIT_START( node );
    1667 
    1668         VISIT(
    1669                 maybe_accept( node, &FunctionType::forall  );
     1740template< typename core_t >
     1741const ast::Type * ast::Pass< core_t >::visit( const ast::FunctionType * node ) {
     1742        VISIT_START( node );
     1743
     1744        VISIT({
     1745                // guard_forall_subs forall_guard { *this, node };
     1746                // mutate_forall( node );
     1747                maybe_accept( node, &FunctionType::assertions );
    16701748                maybe_accept( node, &FunctionType::returns );
    16711749                maybe_accept( node, &FunctionType::params  );
    1672         )
     1750        })
    16731751
    16741752        VISIT_END( Type, node );
     
    16771755//--------------------------------------------------------------------------
    16781756// StructInstType
    1679 template< typename pass_t >
    1680 const ast::Type * ast::Pass< pass_t >::visit( const ast::StructInstType * node ) {
    1681         VISIT_START( node );
    1682 
    1683         __pass::symtab::addStruct( pass, 0, node->name );
     1757template< typename core_t >
     1758const ast::Type * ast::Pass< core_t >::visit( const ast::StructInstType * node ) {
     1759        VISIT_START( node );
     1760
     1761        __pass::symtab::addStruct( core, 0, node->name );
    16841762
    16851763        VISIT({
    16861764                guard_symtab guard { *this };
    1687                 maybe_accept( node, &StructInstType::forall );
    16881765                maybe_accept( node, &StructInstType::params );
    16891766        })
     
    16941771//--------------------------------------------------------------------------
    16951772// UnionInstType
    1696 template< typename pass_t >
    1697 const ast::Type * ast::Pass< pass_t >::visit( const ast::UnionInstType * node ) {
    1698         VISIT_START( node );
    1699 
    1700         __pass::symtab::addStruct( pass, 0, node->name );
    1701 
    1702         {
     1773template< typename core_t >
     1774const ast::Type * ast::Pass< core_t >::visit( const ast::UnionInstType * node ) {
     1775        VISIT_START( node );
     1776
     1777        __pass::symtab::addUnion( core, 0, node->name );
     1778
     1779        VISIT({
    17031780                guard_symtab guard { *this };
    1704                 maybe_accept( node, &UnionInstType::forall );
    17051781                maybe_accept( node, &UnionInstType::params );
    1706         }
     1782        })
    17071783
    17081784        VISIT_END( Type, node );
     
    17111787//--------------------------------------------------------------------------
    17121788// EnumInstType
    1713 template< typename pass_t >
    1714 const ast::Type * ast::Pass< pass_t >::visit( const ast::EnumInstType * node ) {
    1715         VISIT_START( node );
    1716 
    1717         VISIT(
    1718                 maybe_accept( node, &EnumInstType::forall );
     1789template< typename core_t >
     1790const ast::Type * ast::Pass< core_t >::visit( const ast::EnumInstType * node ) {
     1791        VISIT_START( node );
     1792
     1793        VISIT({
    17191794                maybe_accept( node, &EnumInstType::params );
    1720         )
     1795        })
    17211796
    17221797        VISIT_END( Type, node );
     
    17251800//--------------------------------------------------------------------------
    17261801// TraitInstType
    1727 template< typename pass_t >
    1728 const ast::Type * ast::Pass< pass_t >::visit( const ast::TraitInstType * node ) {
    1729         VISIT_START( node );
    1730 
    1731         VISIT(
    1732                 maybe_accept( node, &TraitInstType::forall );
     1802template< typename core_t >
     1803const ast::Type * ast::Pass< core_t >::visit( const ast::TraitInstType * node ) {
     1804        VISIT_START( node );
     1805
     1806        VISIT({
    17331807                maybe_accept( node, &TraitInstType::params );
    1734         )
     1808        })
    17351809
    17361810        VISIT_END( Type, node );
     
    17391813//--------------------------------------------------------------------------
    17401814// TypeInstType
    1741 template< typename pass_t >
    1742 const ast::Type * ast::Pass< pass_t >::visit( const ast::TypeInstType * node ) {
    1743         VISIT_START( node );
    1744 
    1745         VISIT(
    1746                 maybe_accept( node, &TypeInstType::forall );
    1747                 maybe_accept( node, &TypeInstType::params );
     1815template< typename core_t >
     1816const ast::Type * ast::Pass< core_t >::visit( const ast::TypeInstType * node ) {
     1817        VISIT_START( node );
     1818
     1819        VISIT(
     1820                {
     1821                        maybe_accept( node, &TypeInstType::params );
     1822                }
     1823                // ensure that base re-bound if doing substitution
     1824                __pass::forall::replace( core, 0, node );
    17481825        )
    17491826
     
    17531830//--------------------------------------------------------------------------
    17541831// TupleType
    1755 template< typename pass_t >
    1756 const ast::Type * ast::Pass< pass_t >::visit( const ast::TupleType * node ) {
     1832template< typename core_t >
     1833const ast::Type * ast::Pass< core_t >::visit( const ast::TupleType * node ) {
    17571834        VISIT_START( node );
    17581835
     
    17671844//--------------------------------------------------------------------------
    17681845// TypeofType
    1769 template< typename pass_t >
    1770 const ast::Type * ast::Pass< pass_t >::visit( const ast::TypeofType * node ) {
     1846template< typename core_t >
     1847const ast::Type * ast::Pass< core_t >::visit( const ast::TypeofType * node ) {
    17711848        VISIT_START( node );
    17721849
     
    17801857//--------------------------------------------------------------------------
    17811858// VarArgsType
    1782 template< typename pass_t >
    1783 const ast::Type * ast::Pass< pass_t >::visit( const ast::VarArgsType * node ) {
     1859template< typename core_t >
     1860const ast::Type * ast::Pass< core_t >::visit( const ast::VarArgsType * node ) {
    17841861        VISIT_START( node );
    17851862
     
    17891866//--------------------------------------------------------------------------
    17901867// ZeroType
    1791 template< typename pass_t >
    1792 const ast::Type * ast::Pass< pass_t >::visit( const ast::ZeroType * node ) {
     1868template< typename core_t >
     1869const ast::Type * ast::Pass< core_t >::visit( const ast::ZeroType * node ) {
    17931870        VISIT_START( node );
    17941871
     
    17981875//--------------------------------------------------------------------------
    17991876// OneType
    1800 template< typename pass_t >
    1801 const ast::Type * ast::Pass< pass_t >::visit( const ast::OneType * node ) {
     1877template< typename core_t >
     1878const ast::Type * ast::Pass< core_t >::visit( const ast::OneType * node ) {
    18021879        VISIT_START( node );
    18031880
     
    18071884//--------------------------------------------------------------------------
    18081885// GlobalScopeType
    1809 template< typename pass_t >
    1810 const ast::Type * ast::Pass< pass_t >::visit( const ast::GlobalScopeType * node ) {
     1886template< typename core_t >
     1887const ast::Type * ast::Pass< core_t >::visit( const ast::GlobalScopeType * node ) {
    18111888        VISIT_START( node );
    18121889
     
    18171894//--------------------------------------------------------------------------
    18181895// Designation
    1819 template< typename pass_t >
    1820 const ast::Designation * ast::Pass< pass_t >::visit( const ast::Designation * node ) {
     1896template< typename core_t >
     1897const ast::Designation * ast::Pass< core_t >::visit( const ast::Designation * node ) {
    18211898        VISIT_START( node );
    18221899
     
    18281905//--------------------------------------------------------------------------
    18291906// SingleInit
    1830 template< typename pass_t >
    1831 const ast::Init * ast::Pass< pass_t >::visit( const ast::SingleInit * node ) {
     1907template< typename core_t >
     1908const ast::Init * ast::Pass< core_t >::visit( const ast::SingleInit * node ) {
    18321909        VISIT_START( node );
    18331910
     
    18411918//--------------------------------------------------------------------------
    18421919// ListInit
    1843 template< typename pass_t >
    1844 const ast::Init * ast::Pass< pass_t >::visit( const ast::ListInit * node ) {
     1920template< typename core_t >
     1921const ast::Init * ast::Pass< core_t >::visit( const ast::ListInit * node ) {
    18451922        VISIT_START( node );
    18461923
     
    18551932//--------------------------------------------------------------------------
    18561933// ConstructorInit
    1857 template< typename pass_t >
    1858 const ast::Init * ast::Pass< pass_t >::visit( const ast::ConstructorInit * node ) {
     1934template< typename core_t >
     1935const ast::Init * ast::Pass< core_t >::visit( const ast::ConstructorInit * node ) {
    18591936        VISIT_START( node );
    18601937
     
    18701947//--------------------------------------------------------------------------
    18711948// Attribute
    1872 template< typename pass_t >
    1873 const ast::Attribute * ast::Pass< pass_t >::visit( const ast::Attribute * node  )  {
     1949template< typename core_t >
     1950const ast::Attribute * ast::Pass< core_t >::visit( const ast::Attribute * node  )  {
    18741951        VISIT_START( node );
    18751952
     
    18831960//--------------------------------------------------------------------------
    18841961// TypeSubstitution
    1885 template< typename pass_t >
    1886 const ast::TypeSubstitution * ast::Pass< pass_t >::visit( const ast::TypeSubstitution * node ) {
     1962template< typename core_t >
     1963const ast::TypeSubstitution * ast::Pass< core_t >::visit( const ast::TypeSubstitution * node ) {
    18871964        VISIT_START( node );
    18881965
     
    18901967                {
    18911968                        bool mutated = false;
    1892                         std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;
     1969                        std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map;
    18931970                        for ( const auto & p : node->typeEnv ) {
    18941971                                guard_symtab guard { *this };
    18951972                                auto new_node = p.second->accept( *this );
    1896                                 if (new_node != p.second) mutated = false;
     1973                                if (new_node != p.second) mutated = true;
    18971974                                new_map.insert({ p.first, new_node });
    18981975                        }
    18991976                        if (mutated) {
    1900                                 auto new_node = mutate( node );
     1977                                auto new_node = __pass::mutate<core_t>( node );
    19011978                                new_node->typeEnv.swap( new_map );
    19021979                                node = new_node;
    19031980                        }
    19041981                }
    1905 
    1906                 {
    1907                         bool mutated = false;
    1908                         std::unordered_map< std::string, ast::ptr< ast::Expr > > new_map;
    1909                         for ( const auto & p : node->varEnv ) {
    1910                                 guard_symtab guard { *this };
    1911                                 auto new_node = p.second->accept( *this );
    1912                                 if (new_node != p.second) mutated = false;
    1913                                 new_map.insert({ p.first, new_node });
    1914                         }
    1915                         if (mutated) {
    1916                                 auto new_node = mutate( node );
    1917                                 new_node->varEnv.swap( new_map );
    1918                                 node = new_node;
    1919                         }
    1920                 }
    19211982        )
    19221983
  • src/AST/Pass.proto.hpp

    r3c64c668 r58fe85a  
    1717// IWYU pragma: private, include "Pass.hpp"
    1818
     19#include "Common/Stats/Heap.h"
     20
    1921namespace ast {
    20 template<typename pass_type>
     22template<typename core_t>
    2123class Pass;
     24
     25struct TranslationUnit;
     26
     27struct PureVisitor;
    2228
    2329namespace __pass {
     
    8288                };
    8389
    84                 std::stack< cleanup_t > cleanups;
     90                std::stack< cleanup_t, std::vector<cleanup_t> > cleanups;
    8591        };
    8692
     
    111117        /// "Short hand" to check if this is a valid previsit function
    112118        /// Mostly used to make the static_assert look (and print) prettier
    113         template<typename pass_t, typename node_t>
     119        template<typename core_t, typename node_t>
    114120        struct is_valid_previsit {
    115                 using ret_t = decltype( ((pass_t*)nullptr)->previsit( (const node_t *)nullptr ) );
     121                using ret_t = decltype( ((core_t*)nullptr)->previsit( (const node_t *)nullptr ) );
    116122
    117123                static constexpr bool value = std::is_void< ret_t >::value ||
     
    127133        template<>
    128134        struct __assign<true> {
    129                 template<typename pass_t, typename node_t>
    130                 static inline void result( pass_t & pass, const node_t * & node ) {
    131                         pass.previsit( node );
     135                template<typename core_t, typename node_t>
     136                static inline void result( core_t & core, const node_t * & node ) {
     137                        core.previsit( node );
    132138                }
    133139        };
     
    135141        template<>
    136142        struct __assign<false> {
    137                 template<typename pass_t, typename node_t>
    138                 static inline void result( pass_t & pass, const node_t * & node ) {
    139                         node = pass.previsit( node );
     143                template<typename core_t, typename node_t>
     144                static inline void result( core_t & core, const node_t * & node ) {
     145                        node = core.previsit( node );
    140146                        assertf(node, "Previsit must not return NULL");
    141147                }
     
    150156        template<>
    151157        struct __return<true> {
    152                 template<typename pass_t, typename node_t>
    153                 static inline const node_t * result( pass_t & pass, const node_t * & node ) {
    154                         pass.postvisit( node );
     158                template<typename core_t, typename node_t>
     159                static inline const node_t * result( core_t & core, const node_t * & node ) {
     160                        core.postvisit( node );
    155161                        return node;
    156162                }
     
    159165        template<>
    160166        struct __return<false> {
    161                 template<typename pass_t, typename node_t>
    162                 static inline auto result( pass_t & pass, const node_t * & node ) {
    163                         return pass.postvisit( node );
     167                template<typename core_t, typename node_t>
     168                static inline auto result( core_t & core, const node_t * & node ) {
     169                        return core.postvisit( node );
    164170                }
    165171        };
     
    180186        //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    181187        // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call
    182         template<typename pass_t, typename node_t>
    183         static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) {
     188        template<typename core_t, typename node_t>
     189        static inline auto previsit( core_t & core, const node_t * & node, int ) -> decltype( core.previsit( node ), void() ) {
    184190                static_assert(
    185                         is_valid_previsit<pass_t, node_t>::value,
     191                        is_valid_previsit<core_t, node_t>::value,
    186192                        "Previsit may not change the type of the node. It must return its paremeter or void."
    187193                );
     
    189195                __assign<
    190196                        std::is_void<
    191                                 decltype( pass.previsit( node ) )
     197                                decltype( core.previsit( node ) )
    192198                        >::value
    193                 >::result( pass, node );
    194         }
    195 
    196         template<typename pass_t, typename node_t>
    197         static inline auto previsit( pass_t &, const node_t *, long ) {}
     199                >::result( core, node );
     200        }
     201
     202        template<typename core_t, typename node_t>
     203        static inline auto previsit( core_t &, const node_t *, long ) {}
    198204
    199205        // PostVisit : never mutates the passed pointer but may return a different node
    200         template<typename pass_t, typename node_t>
    201         static inline auto postvisit( pass_t & pass, const node_t * node, int ) ->
    202                 decltype( pass.postvisit( node ), node->accept( *(Visitor*)nullptr ) )
     206        template<typename core_t, typename node_t>
     207        static inline auto postvisit( core_t & core, const node_t * node, int ) ->
     208                decltype( core.postvisit( node ), node->accept( *(Visitor*)nullptr ) )
    203209        {
    204210                return __return<
    205211                        std::is_void<
    206                                 decltype( pass.postvisit( node ) )
     212                                decltype( core.postvisit( node ) )
    207213                        >::value
    208                 >::result( pass, node );
    209         }
    210 
    211         template<typename pass_t, typename node_t>
    212         static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; }
     214                >::result( core, node );
     215        }
     216
     217        template<typename core_t, typename node_t>
     218        static inline const node_t * postvisit( core_t &, const node_t * node, long ) { return node; }
    213219
    214220        //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     
    225231        // The type is not strictly enforced but does match the accessory
    226232        #define FIELD_PTR( name, default_type ) \
    227         template< typename pass_t > \
    228         static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \
     233        template< typename core_t > \
     234        static inline auto name( core_t & core, int ) -> decltype( &core.name ) { return &core.name; } \
    229235        \
    230         template< typename pass_t > \
    231         static inline default_type * name( pass_t &, long ) { return nullptr; }
     236        template< typename core_t > \
     237        static inline default_type * name( core_t &, long ) { return nullptr; }
    232238
    233239        // List of fields and their expected types
    234         FIELD_PTR( env, const ast::TypeSubstitution * )
     240        FIELD_PTR( typeSubs, const ast::TypeSubstitution * )
    235241        FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > )
    236242        FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > )
     
    239245        FIELD_PTR( visit_children, __pass::bool_ref )
    240246        FIELD_PTR( at_cleanup, __pass::at_cleanup_t )
    241         FIELD_PTR( visitor, ast::Pass<pass_t> * const )
     247        FIELD_PTR( visitor, ast::Pass<core_t> * const )
    242248
    243249        // Remove the macro to make sure we don't clash
    244250        #undef FIELD_PTR
     251
     252        template< typename core_t >
     253        static inline auto beginTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {
     254                // Stats::Heap::stacktrace_push(core_t::traceId);
     255        }
     256
     257        template< typename core_t >
     258        static inline auto endTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {
     259                // Stats::Heap::stacktrace_pop();
     260        }
     261
     262        template< typename core_t >
     263        static void beginTrace(core_t &, long) {}
     264
     265        template< typename core_t >
     266        static void endTrace(core_t &, long) {}
     267
     268        // Allows visitor to handle an error on top-level declarations, and possibly suppress the error.
     269        // If onError() returns false, the error will be ignored. By default, it returns true.
     270
     271        template< typename core_t >
     272        static bool on_error (core_t &, ptr<Decl> &, long) { return true; }
     273
     274        template< typename core_t >
     275        static auto on_error (core_t & core, ptr<Decl> & decl, int) -> decltype(core.on_error(decl)) {
     276                return core.on_error(decl);
     277        }
    245278
    246279        // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement.
     
    248281        // detect it using the same strategy
    249282        namespace scope {
    250                 template<typename pass_t>
    251                 static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) {
    252                         pass.beginScope();
    253                 }
    254 
    255                 template<typename pass_t>
    256                 static inline void enter( pass_t &, long ) {}
    257 
    258                 template<typename pass_t>
    259                 static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) {
    260                         pass.endScope();
    261                 }
    262 
    263                 template<typename pass_t>
    264                 static inline void leave( pass_t &, long ) {}
    265         };
    266 
    267         // Finally certain pass desire an up to date symbol table automatically
     283                template<typename core_t>
     284                static inline auto enter( core_t & core, int ) -> decltype( core.beginScope(), void() ) {
     285                        core.beginScope();
     286                }
     287
     288                template<typename core_t>
     289                static inline void enter( core_t &, long ) {}
     290
     291                template<typename core_t>
     292                static inline auto leave( core_t & core, int ) -> decltype( core.endScope(), void() ) {
     293                        core.endScope();
     294                }
     295
     296                template<typename core_t>
     297                static inline void leave( core_t &, long ) {}
     298        } // namespace scope
     299
     300        // Certain passes desire an up to date symbol table automatically
    268301        // detect the presence of a member name `symtab` and call all the members appropriately
    269302        namespace symtab {
    270303                // Some simple scoping rules
    271                 template<typename pass_t>
    272                 static inline auto enter( pass_t & pass, int ) -> decltype( pass.symtab.enterScope(), void() ) {
    273                         pass.symtab.enterScope();
    274                 }
    275 
    276                 template<typename pass_t>
    277                 static inline auto enter( pass_t &, long ) {}
    278 
    279                 template<typename pass_t>
    280                 static inline auto leave( pass_t & pass, int ) -> decltype( pass.symtab.leaveScope(), void() ) {
    281                         pass.symtab.leaveScope();
    282                 }
    283 
    284                 template<typename pass_t>
    285                 static inline auto leave( pass_t &, long ) {}
     304                template<typename core_t>
     305                static inline auto enter( core_t & core, int ) -> decltype( core.symtab, void() ) {
     306                        core.symtab.enterScope();
     307                }
     308
     309                template<typename core_t>
     310                static inline auto enter( core_t &, long ) {}
     311
     312                template<typename core_t>
     313                static inline auto leave( core_t & core, int ) -> decltype( core.symtab, void() ) {
     314                        core.symtab.leaveScope();
     315                }
     316
     317                template<typename core_t>
     318                static inline auto leave( core_t &, long ) {}
    286319
    287320                // The symbol table has 2 kind of functions mostly, 1 argument and 2 arguments
    288321                // Create macro to condense these common patterns
    289322                #define SYMTAB_FUNC1( func, type ) \
    290                 template<typename pass_t> \
    291                 static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.symtab.func( arg ), void() ) {\
    292                         pass.symtab.func( arg ); \
     323                template<typename core_t> \
     324                static inline auto func( core_t & core, int, type arg ) -> decltype( core.symtab.func( arg ), void() ) {\
     325                        core.symtab.func( arg ); \
    293326                } \
    294327                \
    295                 template<typename pass_t> \
    296                 static inline void func( pass_t &, long, type ) {}
     328                template<typename core_t> \
     329                static inline void func( core_t &, long, type ) {}
    297330
    298331                #define SYMTAB_FUNC2( func, type1, type2 ) \
    299                 template<typename pass_t> \
    300                 static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.symtab.func( arg1, arg2 ), void () ) {\
    301                         pass.symtab.func( arg1, arg2 ); \
     332                template<typename core_t> \
     333                static inline auto func( core_t & core, int, type1 arg1, type2 arg2 ) -> decltype( core.symtab.func( arg1, arg2 ), void () ) {\
     334                        core.symtab.func( arg1, arg2 ); \
    302335                } \
    303336                        \
    304                 template<typename pass_t> \
    305                 static inline void func( pass_t &, long, type1, type2 ) {}
     337                template<typename core_t> \
     338                static inline void func( core_t &, long, type1, type2 ) {}
    306339
    307340                SYMTAB_FUNC1( addId     , const DeclWithType *  );
     
    311344                SYMTAB_FUNC1( addUnion  , const UnionDecl *     );
    312345                SYMTAB_FUNC1( addTrait  , const TraitDecl *     );
    313                 SYMTAB_FUNC2( addWith   , const std::vector< ptr<Expr> > &, const Node * );
     346                SYMTAB_FUNC2( addWith   , const std::vector< ptr<Expr> > &, const Decl * );
    314347
    315348                // A few extra functions have more complicated behaviour, they are hand written
    316                 template<typename pass_t>
    317                 static inline auto addStructFwd( pass_t & pass, int, const ast::StructDecl * decl ) -> decltype( pass.symtab.addStruct( decl ), void() ) {
     349                template<typename core_t>
     350                static inline auto addStructFwd( core_t & core, int, const ast::StructDecl * decl ) -> decltype( core.symtab.addStruct( decl ), void() ) {
    318351                        ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name );
    319352                        fwd->params = decl->params;
    320                         pass.symtab.addStruct( fwd );
    321                 }
    322 
    323                 template<typename pass_t>
    324                 static inline void addStructFwd( pass_t &, long, const ast::StructDecl * ) {}
    325 
    326                 template<typename pass_t>
    327                 static inline auto addUnionFwd( pass_t & pass, int, const ast::UnionDecl * decl ) -> decltype( pass.symtab.addUnion( decl ), void() ) {
     353                        core.symtab.addStruct( fwd );
     354                }
     355
     356                template<typename core_t>
     357                static inline void addStructFwd( core_t &, long, const ast::StructDecl * ) {}
     358
     359                template<typename core_t>
     360                static inline auto addUnionFwd( core_t & core, int, const ast::UnionDecl * decl ) -> decltype( core.symtab.addUnion( decl ), void() ) {
    328361                        UnionDecl * fwd = new UnionDecl( decl->location, decl->name );
    329362                        fwd->params = decl->params;
    330                         pass.symtab.addUnion( fwd );
    331                 }
    332 
    333                 template<typename pass_t>
    334                 static inline void addUnionFwd( pass_t &, long, const ast::UnionDecl * ) {}
    335 
    336                 template<typename pass_t>
    337                 static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.symtab.addStruct( str ), void() ) {
    338                         if ( ! pass.symtab.lookupStruct( str ) ) {
    339                                 pass.symtab.addStruct( str );
     363                        core.symtab.addUnion( fwd );
     364                }
     365
     366                template<typename core_t>
     367                static inline void addUnionFwd( core_t &, long, const ast::UnionDecl * ) {}
     368
     369                template<typename core_t>
     370                static inline auto addStruct( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addStruct( str ), void() ) {
     371                        if ( ! core.symtab.lookupStruct( str ) ) {
     372                                core.symtab.addStruct( str );
    340373                        }
    341374                }
    342375
    343                 template<typename pass_t>
    344                 static inline void addStruct( pass_t &, long, const std::string & ) {}
    345 
    346                 template<typename pass_t>
    347                 static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.symtab.addUnion( str ), void() ) {
    348                         if ( ! pass.symtab.lookupUnion( str ) ) {
    349                                 pass.symtab.addUnion( str );
     376                template<typename core_t>
     377                static inline void addStruct( core_t &, long, const std::string & ) {}
     378
     379                template<typename core_t>
     380                static inline auto addUnion( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addUnion( str ), void() ) {
     381                        if ( ! core.symtab.lookupUnion( str ) ) {
     382                                core.symtab.addUnion( str );
    350383                        }
    351384                }
    352385
    353                 template<typename pass_t>
    354                 static inline void addUnion( pass_t &, long, const std::string & ) {}
     386                template<typename core_t>
     387                static inline void addUnion( core_t &, long, const std::string & ) {}
    355388
    356389                #undef SYMTAB_FUNC1
    357390                #undef SYMTAB_FUNC2
    358         };
    359 };
    360 };
     391        } // namespace symtab
     392
     393        // Some passes need to mutate TypeDecl and properly update their pointing TypeInstType.
     394        // Detect the presence of a member name `subs` and call all members appropriately
     395        namespace forall {
     396                // Some simple scoping rules
     397                template<typename core_t>
     398                static inline auto enter( core_t & core, int, const ast::FunctionType * type )
     399                -> decltype( core.subs, void() ) {
     400                        if ( ! type->forall.empty() ) core.subs.beginScope();
     401                }
     402
     403                template<typename core_t>
     404                static inline auto enter( core_t &, long, const ast::FunctionType * ) {}
     405
     406                template<typename core_t>
     407                static inline auto leave( core_t & core, int, const ast::FunctionType * type )
     408                -> decltype( core.subs, void() ) {
     409                        if ( ! type->forall.empty() ) { core.subs.endScope(); }
     410                }
     411
     412                template<typename core_t>
     413                static inline auto leave( core_t &, long, const ast::FunctionType * ) {}
     414
     415                // Replaces a TypeInstType's base TypeDecl according to the table
     416                template<typename core_t>
     417                static inline auto replace( core_t & core, int, const ast::TypeInstType *& inst )
     418                -> decltype( core.subs, void() ) {
     419                        inst = ast::mutate_field(
     420                                inst, &ast::TypeInstType::base, core.subs.replace( inst->base ) );
     421                }
     422
     423                template<typename core_t>
     424                static inline auto replace( core_t &, long, const ast::TypeInstType *& ) {}
     425
     426        } // namespace forall
     427
     428        template<typename core_t>
     429        static inline auto get_result( core_t & core, char ) -> decltype( core.result() ) {
     430                return core.result();
     431        }
     432
     433        template<typename core_t>
     434        static inline auto get_result( core_t & core, int ) -> decltype( core.result ) {
     435                return core.result;
     436        }
     437
     438        template<typename core_t>
     439        static inline void get_result( core_t &, long ) {}
     440} // namespace __pass
     441} // namespace ast
  • src/AST/Print.cpp

    r3c64c668 r58fe85a  
    2121#include "Type.hpp"
    2222#include "TypeSubstitution.hpp"
     23#include "CompilationState.h"
    2324
    2425#include "Common/utility.h" // for group_iterate
     
    2930
    3031template <typename C, typename... T>
    31 constexpr auto make_array(T&&... values) ->
    32         array<C,sizeof...(T)>
     32constexpr array<C,sizeof...(T)> make_array(T&&... values)
    3333{
    3434        return array<C,sizeof...(T)>{
     
    129129
    130130        void print( const ast::Expr::InferUnion & inferred, unsigned level = 0 ) {
    131                 switch ( inferred.mode ) {
    132                 case ast::Expr::InferUnion::Empty: return;
    133                 case ast::Expr::InferUnion::Slots: {
    134                         os << indent << "with " << inferred.data.resnSlots.size()
     131                if (inferred.data.resnSlots && !inferred.data.resnSlots->empty()) {
     132                        os << indent << "with " << inferred.data.resnSlots->size()
    135133                           << " pending inference slots" << endl;
    136                         return;
    137                 }
    138                 case ast::Expr::InferUnion::Params: {
     134                }
     135                if (inferred.data.inferParams && !inferred.data.inferParams->empty()) {
    139136                        os << indent << "with inferred parameters " << level << ":" << endl;
    140137                        ++indent;
    141                         for ( const auto & i : inferred.data.inferParams ) {
     138                        for ( const auto & i : *inferred.data.inferParams ) {
    142139                                os << indent;
    143                                 short_print( Decl::fromId( i.second.decl ) );
     140                                short_print( i.second.declptr );
    144141                                os << endl;
    145142                                print( i.second.expr->inferred, level+1 );
    146143                        }
    147144                        --indent;
    148                         return;
    149                 }
    150                 }
    151         }
    152 
    153         void print( const ast::ParameterizedType::ForallList & forall ) {
     145                }
     146        }
     147
     148        void print( const ast::FunctionType::ForallList & forall ) {
    154149                if ( forall.empty() ) return;
    155150                os << "forall" << endl;
    156151                ++indent;
    157152                printAll( forall );
     153                os << indent;
     154                --indent;
     155        }
     156
     157        void print( const ast::FunctionType::AssertionList & assts ) {
     158                if (assts.empty()) return;
     159                os << "with assertions" << endl;
     160                ++indent;
     161                printAll(assts);
    158162                os << indent;
    159163                --indent;
     
    210214
    211215        void preprint( const ast::NamedTypeDecl * node ) {
    212                 if ( ! node->name.empty() ) os << node->name << ": ";
     216                if ( ! node->name.empty() ) {
     217                        os << node->name << ": ";
     218                }
    213219
    214220                if ( ! short_mode && node->linkage != Linkage::Cforall ) {
     
    226232                }
    227233
    228                 if ( ! node->params.empty() ) {
    229                         os << endl << indent << "... with parameters" << endl;
    230                         ++indent;
    231                         printAll( node->params );
    232                         --indent;
    233                 }
    234 
    235                 if ( ! short_mode && ! node->assertions.empty() ) {
     234                if ( ! node->assertions.empty() ) {
    236235                        os << endl << indent << "... with assertions" << endl;
    237236                        ++indent;
     
    244243                print( node->inferred );
    245244
     245                if ( node->result ) {
     246                        os << endl << indent << "... with resolved type:" << endl;
     247                        ++indent;
     248                        os << indent;
     249                        node->result->accept( *this );
     250                        --indent;
     251                }
     252
    246253                if ( node->env ) {
    247254                        os << endl << indent << "... with environment:" << endl;
     
    260267        }
    261268
    262         void preprint( const ast::ParameterizedType * node ) {
     269        void preprint( const ast::FunctionType * node ) {
    263270                print( node->forall );
     271                print( node->assertions );
    264272                print( node->qualifiers );
    265273        }
    266274
    267         void preprint( const ast::ReferenceToType * node ) {
    268                 print( node->forall );
     275        void preprint( const ast::BaseInstType * node ) {
    269276                print( node->attributes );
    270277                print( node->qualifiers );
     
    678685        }
    679686
     687        virtual const ast::Stmt * visit( const ast::SuspendStmt * node ) override final {
     688                os << "Suspend Statement";
     689                switch (node->type) {
     690                        case ast::SuspendStmt::None     : os << " with implicit target"; break;
     691                        case ast::SuspendStmt::Generator: os << " for generator"; break;
     692                        case ast::SuspendStmt::Coroutine: os << " for coroutine"; break;
     693                }
     694                os << endl;
     695
     696                ++indent;
     697                if(node->then) {
     698                        os << indent << " with post statement :" << endl;
     699                        safe_print( node->then );
     700                }
     701                ++indent;
     702
     703                return node;
     704        }
     705
    680706        virtual const ast::Stmt * visit( const ast::WaitForStmt * node ) override final {
    681707                os << "Waitfor Statement" << endl;
     
    823849        virtual const ast::Expr * visit( const ast::CastExpr * node ) override final {
    824850                ++indent;
    825                 os << (node->isGenerated ? "Generated" : "Explicit") << " cast of:" << endl << indent;
     851                os << (node->isGenerated ? "Generated" : "Explicit") << " Cast of:" << endl << indent;
    826852                safe_print( node->arg );
    827853                os << endl << indent-1 << "... to:";
     
    13581384        virtual const ast::Type * visit( const ast::TypeInstType * node ) override final {
    13591385                preprint( node );
    1360                 os << "instance of type " << node->name
     1386                const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->typeString();
     1387                os << "instance of type " << _name
    13611388                   << " (" << (node->kind == ast::TypeDecl::Ftype ? "" : "not ") << "function type)";
    13621389                print( node->params );
     
    14841511                os << indent << "Types:" << endl;
    14851512                for ( const auto& i : *node ) {
    1486                         os << indent+1 << i.first << " -> ";
     1513                        os << indent+1 << i.first.typeString() << " -> ";
    14871514                        indent += 2;
    14881515                        safe_print( i.second );
    1489                         indent -= 2;
    1490                         os << endl;
    1491                 }
    1492                 os << indent << "Non-types:" << endl;
    1493                 for ( auto i = node->beginVar(); i != node->endVar(); ++i ) {
    1494                         os << indent+1 << i->first << " -> ";
    1495                         indent += 2;
    1496                         safe_print( i->second );
    14971516                        indent -= 2;
    14981517                        os << endl;
  • src/AST/Stmt.hpp

    r3c64c668 r58fe85a  
    2727
    2828// Must be included in *all* AST classes; should be #undef'd at the end of the file
    29 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
     29#define MUTATE_FRIEND \
     30    template<typename node_t> friend node_t * mutate(const node_t * node); \
     31        template<typename node_t> friend node_t * shallowCopy(const node_t * node);
    3032
    3133namespace ast {
     
    342344};
    343345
     346/// Suspend statement
     347class SuspendStmt final : public Stmt {
     348public:
     349        ptr<CompoundStmt> then;
     350        enum Type { None, Coroutine, Generator } type = None;
     351
     352        SuspendStmt( const CodeLocation & loc, const CompoundStmt * then, Type type, std::vector<Label> && labels = {} )
     353        : Stmt(loc, std::move(labels)), then(then), type(type) {}
     354
     355        const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
     356private:
     357        SuspendStmt * clone() const override { return new SuspendStmt{ *this }; }
     358        MUTATE_FRIEND
     359};
     360
    344361/// Wait for concurrency statement `when (...) waitfor (... , ...) ... timeout(...) ... else ...`
    345362class WaitForStmt final : public Stmt {
     
    397414class ImplicitCtorDtorStmt final : public Stmt {
    398415public:
    399         readonly<Stmt> callStmt;
     416        ptr<Stmt> callStmt;
    400417
    401418        ImplicitCtorDtorStmt( const CodeLocation & loc, const Stmt * callStmt,
  • src/AST/SymbolTable.cpp

    r3c64c668 r58fe85a  
    9595}
    9696
     97SymbolTable::SpecialFunctionKind SymbolTable::getSpecialFunctionKind(const std::string & name) {
     98        if (name == "?{}") return CTOR;
     99        if (name == "^?{}") return DTOR;
     100        if (name == "?=?") return ASSIGN;
     101        return NUMBER_OF_KINDS;
     102}
     103
    97104std::vector<SymbolTable::IdData> SymbolTable::lookupId( const std::string &id ) const {
     105        static Stats::Counters::CounterGroup * name_lookup_stats = Stats::Counters::build<Stats::Counters::CounterGroup>("Name Lookup Stats");
     106        static std::map<std::string, Stats::Counters::SimpleCounter *> lookups_by_name;
     107        static std::map<std::string, Stats::Counters::SimpleCounter *> candidates_by_name;
     108
     109        SpecialFunctionKind kind = getSpecialFunctionKind(id);
     110        if (kind != NUMBER_OF_KINDS) return specialLookupId(kind);
     111
    98112        ++*stats().lookup_calls;
    99113        if ( ! idTable ) return {};
     
    107121                out.push_back( decl.second );
    108122        }
     123
     124        if (Stats::Counters::enabled) {
     125                if (! lookups_by_name.count(id)) {
     126                        // leaks some strings, but it is because Counters do not hold them
     127                        auto lookupCounterName = new std::string(id + "%count");
     128                        auto candidatesCounterName = new std::string(id + "%candidate");
     129                        lookups_by_name.emplace(id, new Stats::Counters::SimpleCounter(lookupCounterName->c_str(), name_lookup_stats));
     130                        candidates_by_name.emplace(id, new Stats::Counters::SimpleCounter(candidatesCounterName->c_str(), name_lookup_stats));
     131                }
     132                (*lookups_by_name[id]) ++;
     133                *candidates_by_name[id] += out.size();
     134        }
     135
     136        return out;
     137}
     138
     139std::vector<SymbolTable::IdData> SymbolTable::specialLookupId( SymbolTable::SpecialFunctionKind kind, const std::string & otypeKey ) const {
     140        static Stats::Counters::CounterGroup * special_stats = Stats::Counters::build<Stats::Counters::CounterGroup>("Special Lookups");
     141        static Stats::Counters::SimpleCounter * stat_counts[3] = {
     142                Stats::Counters::build<Stats::Counters::SimpleCounter>("constructor - count", special_stats),
     143                Stats::Counters::build<Stats::Counters::SimpleCounter>("destructor - count", special_stats),
     144                Stats::Counters::build<Stats::Counters::SimpleCounter>("assignment - count", special_stats)
     145        };
     146
     147        static Stats::Counters::SimpleCounter * stat_candidates[3] = {
     148                Stats::Counters::build<Stats::Counters::SimpleCounter>("constructor - candidates", special_stats),
     149                Stats::Counters::build<Stats::Counters::SimpleCounter>("destructor - candidates", special_stats),
     150                Stats::Counters::build<Stats::Counters::SimpleCounter>("assignment - candidates", special_stats)
     151        };
     152
     153        static Stats::Counters::SimpleCounter * num_lookup_with_key
     154                = Stats::Counters::build<Stats::Counters::SimpleCounter>("keyed lookups", special_stats);
     155        static Stats::Counters::SimpleCounter * num_lookup_without_key
     156                = Stats::Counters::build<Stats::Counters::SimpleCounter>("unkeyed lookups", special_stats);
     157
     158        assert (kind != NUMBER_OF_KINDS);
     159        ++*stats().lookup_calls;
     160        if ( ! specialFunctionTable[kind] ) return {};
     161
     162        std::vector<IdData> out;
     163
     164        if (otypeKey.empty()) { // returns everything
     165                ++*num_lookup_without_key;
     166                for (auto & table : *specialFunctionTable[kind]) {
     167                        for (auto & decl : *table.second) {
     168                                out.push_back(decl.second);
     169                        }
     170                }
     171        }
     172        else {
     173                ++*num_lookup_with_key;
     174                ++*stats().map_lookups;
     175                auto decls = specialFunctionTable[kind]->find(otypeKey);
     176                if (decls == specialFunctionTable[kind]->end()) return {};
     177
     178                for (auto decl : *(decls->second)) {
     179                        out.push_back(decl.second);
     180                }
     181        }
     182
     183        ++*stat_counts[kind];
     184        *stat_candidates[kind] += out.size();
     185
    109186        return out;
    110187}
     
    313390                if ( ! expr->result ) continue;
    314391                const Type * resTy = expr->result->stripReferences();
    315                 auto aggrType = dynamic_cast< const ReferenceToType * >( resTy );
     392                auto aggrType = dynamic_cast< const BaseInstType * >( resTy );
    316393                assertf( aggrType, "WithStmt expr has non-aggregate type: %s",
    317394                        toString( expr->result ).c_str() );
     
    335412}
    336413
    337 void SymbolTable::addFunctionType( const FunctionType * ftype ) {
    338         addTypes( ftype->forall );
    339         addIds( ftype->returns );
    340         addIds( ftype->params );
    341 }
     414
     415void SymbolTable::addFunction( const FunctionDecl * func ) {
     416        for (auto & td : func->type_params) {
     417                addType(td);
     418        }
     419        for (auto & asst : func->assertions) {
     420                addId(asst);
     421        }
     422        // addTypes( func->type->forall );
     423        addIds( func->returns );
     424        addIds( func->params );
     425}
     426
    342427
    343428void SymbolTable::lazyInitScope() {
     
    364449namespace {
    365450        /// gets the base type of the first parameter; decl must be a ctor/dtor/assignment function
    366         std::string getOtypeKey( const FunctionDecl * function ) {
    367                 const auto & params = function->type->params;
     451        std::string getOtypeKey( const FunctionType * ftype, bool stripParams = true ) {
     452                const auto & params = ftype->params;
    368453                assert( ! params.empty() );
    369454                // use base type of pointer, so that qualifiers on the pointer type aren't considered.
    370                 const Type * base = InitTweak::getPointerBase( params.front()->get_type() );
     455                const Type * base = InitTweak::getPointerBase( params.front() );
    371456                assert( base );
    372                 return Mangle::mangle( base );
     457                if (stripParams) {
     458                        if (dynamic_cast<const PointerType *>(base)) return Mangle::Encoding::pointer;
     459                        return Mangle::mangle( base, Mangle::Type | Mangle::NoGenericParams );
     460                }
     461                else
     462                        return Mangle::mangle( base ); 
    373463        }
    374464
     
    378468                        const DeclWithType * decl, const std::string & otypeKey ) {
    379469                auto func = dynamic_cast< const FunctionDecl * >( decl );
    380                 if ( ! func || otypeKey != getOtypeKey( func ) ) return nullptr;
     470                if ( ! func || otypeKey != getOtypeKey( func->type, false ) ) return nullptr;
    381471                return func;
    382472        }
     
    403493        bool dataIsUserDefinedFunc = ! function->linkage.is_overrideable;
    404494        bool dataIsCopyFunc = InitTweak::isCopyFunction( function );
    405         std::string dataOtypeKey = getOtypeKey( function );
     495        std::string dataOtypeKey = getOtypeKey( function->type, false ); // requires exact match to override autogen
    406496
    407497        if ( dataIsUserDefinedFunc && dataIsCopyFunc ) {
     
    575665                const DeclWithType * decl, SymbolTable::OnConflict handleConflicts, const Expr * baseExpr,
    576666                const Decl * deleter ) {
     667        SpecialFunctionKind kind = getSpecialFunctionKind(decl->name);
     668        if (kind == NUMBER_OF_KINDS) { // not a special decl
     669                addId(decl, decl->name, idTable, handleConflicts, baseExpr, deleter);
     670        }
     671        else {
     672                std::string key;
     673                if (auto func = dynamic_cast<const FunctionDecl *>(decl)) {
     674                        key = getOtypeKey(func->type);
     675                }
     676                else if (auto obj = dynamic_cast<const ObjectDecl *>(decl)) {
     677                        key = getOtypeKey(obj->type.strict_as<PointerType>()->base.strict_as<FunctionType>());
     678                }
     679                else {
     680                        assertf(false, "special decl with non-function type");
     681                }
     682                addId(decl, key, specialFunctionTable[kind], handleConflicts, baseExpr, deleter);
     683        }
     684}
     685
     686void SymbolTable::addId(
     687                const DeclWithType * decl, const std::string & lookupKey, IdTable::Ptr & table, SymbolTable::OnConflict handleConflicts, const Expr * baseExpr,
     688                const Decl * deleter ) {
    577689        ++*stats().add_calls;
    578690        const std::string &name = decl->name;
     
    605717        // ensure tables exist and add identifier
    606718        MangleTable::Ptr mangleTable;
    607         if ( ! idTable ) {
    608                 idTable = IdTable::new_ptr();
     719        if ( ! table ) {
     720                table = IdTable::new_ptr();
    609721                mangleTable = MangleTable::new_ptr();
    610722        } else {
    611723                ++*stats().map_lookups;
    612                 auto decls = idTable->find( name );
    613                 if ( decls == idTable->end() ) {
     724                auto decls = table->find( lookupKey );
     725                if ( decls == table->end() ) {
    614726                        mangleTable = MangleTable::new_ptr();
    615727                } else {
     
    626738                                                lazyInitScope();
    627739                                                *stats().map_mutations += 2;
    628                                                 idTable = idTable->set(
    629                                                         name,
     740                                                table = table->set(
     741                                                        lookupKey,
    630742                                                        mangleTable->set(
    631743                                                                mangleName,
     
    642754        IdData data{ decl, baseExpr, deleter, scope };
    643755        // Ensure that auto-generated ctor/dtor/assignment are deleted if necessary
    644         if ( ! removeSpecialOverrides( data, mangleTable ) ) return;
     756        if (table != idTable) { // adding to special table
     757                if ( ! removeSpecialOverrides( data, mangleTable ) ) return;
     758        }
    645759        *stats().map_mutations += 2;
    646         idTable = idTable->set( name, mangleTable->set( mangleName, std::move(data) ) );
     760        table = table->set( lookupKey, mangleTable->set( mangleName, std::move(data) ) );
    647761}
    648762
     
    654768                        if ( dwt->name == "" ) {
    655769                                const Type * t = dwt->get_type()->stripReferences();
    656                                 if ( auto rty = dynamic_cast<const ReferenceToType *>( t ) ) {
     770                                if ( auto rty = dynamic_cast<const BaseInstType *>( t ) ) {
    657771                                        if ( ! dynamic_cast<const StructInstType *>(rty)
    658772                                                && ! dynamic_cast<const UnionInstType *>(rty) ) continue;
  • src/AST/SymbolTable.hpp

    r3c64c668 r58fe85a  
    3333class SymbolTable final : public std::enable_shared_from_this<ast::SymbolTable> {
    3434public:
     35        /// special functions stored in dedicated tables, with different lookup keys
     36        enum SpecialFunctionKind {CTOR, DTOR, ASSIGN, NUMBER_OF_KINDS};
     37        static SpecialFunctionKind getSpecialFunctionKind(const std::string & name);
     38
    3539        /// Stored information about a declaration
    3640        struct IdData {
     
    7781        UnionTable::Ptr unionTable;    ///< union namespace
    7882        TraitTable::Ptr traitTable;    ///< trait namespace
     83        IdTable::Ptr specialFunctionTable[NUMBER_OF_KINDS];
     84
     85        // using SpecialFuncTable = PersistentMap< std::string, IdTable::Ptr >; // fname (ctor/dtor/assign) - otypekey
     86        // SpecialFuncTable::Ptr specialFuncTable;
    7987
    8088        using Ptr = std::shared_ptr<const SymbolTable>;
     
    95103        /// Gets all declarations with the given ID
    96104        std::vector<IdData> lookupId( const std::string &id ) const;
     105        /// Gets special functions associated with a type; if no key is given, returns everything
     106        std::vector<IdData> specialLookupId( SpecialFunctionKind kind, const std::string & otypeKey = "" ) const;
    97107        /// Gets the top-most type declaration with the given ID
    98108        const NamedTypeDecl * lookupType( const std::string &id ) const;
     
    145155
    146156        /// convenience function for adding all of the declarations in a function type to the indexer
    147         void addFunctionType( const FunctionType * ftype );
     157        void addFunction( const FunctionDecl * );
    148158
    149159private:
     
    186196                const Decl * deleter = nullptr );
    187197
     198        /// common code for addId when special decls are placed into separate tables
     199        void addId(
     200                const DeclWithType * decl, const std::string & lookupKey, IdTable::Ptr & idTable, OnConflict handleConflicts,
     201                const Expr * baseExpr = nullptr, const Decl * deleter = nullptr);
     202       
    188203        /// adds all of the members of the Aggregate (addWith helper)
    189204        void addMembers( const AggregateDecl * aggr, const Expr * expr, OnConflict handleConflicts );
  • src/AST/Type.cpp

    r3c64c668 r58fe85a  
    99// Author           : Aaron B. Moss
    1010// Created On       : Mon May 13 15:00:00 2019
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Dec 15 16:56:28 2019
    13 // Update Count     : 4
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Thu Jul 23 14:16:00 2020
     13// Update Count     : 5
    1414//
    1515
     
    2222#include "Decl.hpp"
    2323#include "Init.hpp"
     24#include "Common/utility.h"      // for copy, move
    2425#include "InitTweak/InitTweak.h" // for getPointerBase
    2526#include "Tuples/Tuples.h"       // for isTtype
     
    9192
    9293// --- FunctionType
    93 
    9494namespace {
    95         bool containsTtype( const std::vector<ptr<DeclWithType>> & l ) {
     95        bool containsTtype( const std::vector<ptr<Type>> & l ) {
    9696                if ( ! l.empty() ) {
    97                         return Tuples::isTtype( l.back()->get_type() );
     97                        return Tuples::isTtype( l.back() );
    9898                }
    9999                return false;
     
    105105}
    106106
    107 // --- ReferenceToType
    108 std::vector<readonly<Decl>> ReferenceToType::lookup( const std::string& name ) const {
     107std::vector<readonly<Decl>> BaseInstType::lookup( const std::string& name ) const {
    109108        assertf( aggr(), "Must have aggregate to perform lookup" );
    110109
     
    116115}
    117116
    118 // --- StructInstType
     117// --- SueInstType (StructInstType, UnionInstType, EnumInstType)
    119118
    120 StructInstType::StructInstType( const StructDecl * b, CV::Qualifiers q,
    121         std::vector<ptr<Attribute>>&& as )
    122 : ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
     119template<typename decl_t>
     120SueInstType<decl_t>::SueInstType(
     121        const decl_t * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
     122: BaseInstType( b->name, q, move(as) ), base( b ) {}
    123123
    124 bool StructInstType::isComplete() const { return base ? base->body : false; }
     124template<typename decl_t>
     125SueInstType<decl_t>::SueInstType(
     126        const base_type * b, std::vector<ptr<Expr>> && params,
     127        CV::Qualifiers q, std::vector<ptr<Attribute>> && as )
     128: BaseInstType( b->name, std::move(params), q, std::move(as) ), base( b ) {}
    125129
    126 // --- UnionInstType
     130template<typename decl_t>
     131bool SueInstType<decl_t>::isComplete() const {
     132        return base ? base->body : false;
     133}
    127134
    128 UnionInstType::UnionInstType( const UnionDecl * b, CV::Qualifiers q,
    129         std::vector<ptr<Attribute>>&& as )
    130 : ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
    131 
    132 bool UnionInstType::isComplete() const { return base ? base->body : false; }
    133 
    134 // --- EnumInstType
    135 
    136 EnumInstType::EnumInstType( const EnumDecl * b, CV::Qualifiers q,
    137         std::vector<ptr<Attribute>>&& as )
    138 : ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
    139 
    140 bool EnumInstType::isComplete() const { return base ? base->body : false; }
     135template class SueInstType<StructDecl>;
     136template class SueInstType<UnionDecl>;
     137template class SueInstType<EnumDecl>;
    141138
    142139// --- TraitInstType
    143140
    144 TraitInstType::TraitInstType( const TraitDecl * b, CV::Qualifiers q,
    145         std::vector<ptr<Attribute>>&& as )
    146 : ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
    147 
    148 // --- TypeInstType
     141TraitInstType::TraitInstType(
     142        const TraitDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
     143: BaseInstType( b->name, q, move(as) ), base( b ) {}
    149144
    150145void TypeInstType::set_base( const TypeDecl * b ) {
     
    158153
    159154TupleType::TupleType( std::vector<ptr<Type>> && ts, CV::Qualifiers q )
    160 : Type( q ), types( std::move(ts) ), members() {
     155: Type( q ), types( move(ts) ), members() {
    161156        // This constructor is awkward. `TupleType` needs to contain objects so that members can be
    162157        // named, but members without initializer nodes end up getting constructors, which breaks
     
    173168        for ( const Type * ty : types ) {
    174169                members.emplace_back( new ObjectDecl{
    175                         CodeLocation{}, "", ty, new ListInit( CodeLocation{}, {}, {}, MaybeConstruct ),
     170                        CodeLocation{}, "", ty, new ListInit( CodeLocation{}, {}, {}, NoConstruct ),
    176171                        Storage::Classes{}, Linkage::Cforall } );
    177172        }
     173}
     174
     175bool isUnboundType(const Type * type) {
     176        if (auto typeInst = dynamic_cast<const TypeInstType *>(type)) {
     177                // xxx - look for a type name produced by renameTyVars.
     178
     179                // TODO: once TypeInstType representation is updated, it should properly check
     180                // if the context id is filled. this is a temporary hack for now
     181                return typeInst->formal_usage > 0;
     182        }
     183        return false;
    178184}
    179185
  • src/AST/Type.hpp

    r3c64c668 r58fe85a  
    99// Author           : Aaron B. Moss
    1010// Created On       : Thu May 9 10:00:00 2019
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Dec 11 21:56:46 2019
    13 // Update Count     : 5
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Thu Jul 23 14:15:00 2020
     13// Update Count     : 6
    1414//
    1515
     
    2929
    3030// Must be included in *all* AST classes; should be #undef'd at the end of the file
    31 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
     31#define MUTATE_FRIEND \
     32    template<typename node_t> friend node_t * mutate(const node_t * node); \
     33        template<typename node_t> friend node_t * shallowCopy(const node_t * node);
    3234
    3335namespace ast {
     36
     37template< typename T > class Pass;
    3438
    3539class Type : public Node {
     
    4448        bool is_volatile() const { return qualifiers.is_volatile; }
    4549        bool is_restrict() const { return qualifiers.is_restrict; }
    46         bool is_lvalue() const { return qualifiers.is_lvalue; }
    4750        bool is_mutex() const { return qualifiers.is_mutex; }
    4851        bool is_atomic() const { return qualifiers.is_atomic; }
     
    5154        Type * set_volatile( bool v ) { qualifiers.is_volatile = v; return this; }
    5255        Type * set_restrict( bool v ) { qualifiers.is_restrict = v; return this; }
    53         Type * set_lvalue( bool v ) { qualifiers.is_lvalue = v; return this; }
    5456        Type * set_mutex( bool v ) { qualifiers.is_mutex = v; return this; }
    5557        Type * set_atomic( bool v ) { qualifiers.is_atomic = v; return this; }
     
    163165        static const char *typeNames[];
    164166
    165         BasicType( Kind k, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 
     167        BasicType( Kind k, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
    166168        : Type(q, std::move(as)), kind(k) {}
    167169
     
    263265};
    264266
    265 /// Base type for potentially forall-qualified types
    266 class ParameterizedType : public Type {
    267 public:
    268         using ForallList = std::vector<ptr<TypeDecl>>;
    269 
    270         ForallList forall;
    271 
    272         ParameterizedType( ForallList&& fs = {}, CV::Qualifiers q = {},
    273                 std::vector<ptr<Attribute>> && as = {} )
    274         : Type(q, std::move(as)), forall(std::move(fs)) {}
    275 
    276         ParameterizedType( CV::Qualifiers q, std::vector<ptr<Attribute>> && as = {} )
    277         : Type(q, std::move(as)), forall() {}
    278 
    279 private:
    280         virtual ParameterizedType * clone() const override = 0;
    281         MUTATE_FRIEND
    282 };
    283 
    284267/// Function variable arguments flag
    285268enum ArgumentFlag { FixedArgs, VariableArgs };
    286269
    287270/// Type of a function `[R1, R2](*)(P1, P2, P3)`
    288 class FunctionType final : public ParameterizedType {
    289 public:
    290         std::vector<ptr<DeclWithType>> returns;
    291         std::vector<ptr<DeclWithType>> params;
     271class FunctionType final : public Type {
     272public:
     273        using ForallList = std::vector<ptr<TypeInstType>>;
     274        using AssertionList = std::vector<ptr<VariableExpr>>;
     275        ForallList forall;
     276        AssertionList assertions;
     277
     278        std::vector<ptr<Type>> returns;
     279        std::vector<ptr<Type>> params;
    292280
    293281        /// Does the function accept a variable number of arguments following the arguments specified
     
    299287
    300288        FunctionType( ArgumentFlag va = FixedArgs, CV::Qualifiers q = {} )
    301         : ParameterizedType(q), returns(), params(), isVarArgs(va) {}
     289        : Type(q), returns(), params(), isVarArgs(va) {}
     290
     291        FunctionType( const FunctionType & o ) = default;
    302292
    303293        /// true if either the parameters or return values contain a tttype
     
    313303
    314304/// base class for types that refer to types declared elsewhere (aggregates and typedefs)
    315 class ReferenceToType : public ParameterizedType {
     305class BaseInstType : public Type {
    316306public:
    317307        std::vector<ptr<Expr>> params;
     
    319309        bool hoistType = false;
    320310
    321         ReferenceToType( const std::string& n, CV::Qualifiers q = {},
    322                 std::vector<ptr<Attribute>> && as = {} )
    323         : ParameterizedType(q, std::move(as)), params(), name(n) {}
     311        BaseInstType(
     312                const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
     313        : Type(q, std::move(as)), params(), name(n) {}
     314
     315        BaseInstType(
     316                const std::string& n, std::vector<ptr<Expr>> && params,
     317                CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
     318        : Type(q, std::move(as)), params(std::move(params)), name(n) {}
     319
     320        BaseInstType( const BaseInstType & o ) = default;
    324321
    325322        /// Gets aggregate declaration this type refers to
     
    329326
    330327private:
    331         virtual ReferenceToType * clone() const override = 0;
    332         MUTATE_FRIEND
    333 };
    334 
    335 /// instance of struct type
    336 class StructInstType final : public ReferenceToType {
    337 public:
    338         readonly<StructDecl> base;
    339 
    340         StructInstType( const std::string& n, CV::Qualifiers q = {},
    341                 std::vector<ptr<Attribute>> && as = {} )
    342         : ReferenceToType( n, q, std::move(as) ), base() {}
    343         StructInstType( const StructDecl * b, CV::Qualifiers q = {},
    344                 std::vector<ptr<Attribute>> && as = {} );
     328        virtual BaseInstType * clone() const override = 0;
     329        MUTATE_FRIEND
     330};
     331
     332// Common implementation for the SUE instance types. Not to be used directly.
     333template<typename decl_t>
     334class SueInstType final : public BaseInstType {
     335public:
     336        using base_type = decl_t;
     337        readonly<decl_t> base;
     338
     339        SueInstType(
     340                const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
     341        : BaseInstType( n, q, std::move(as) ), base() {}
     342
     343        SueInstType(
     344                const base_type * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
     345
     346        SueInstType(
     347                const base_type * b, std::vector<ptr<Expr>> && params,
     348                CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
    345349
    346350        bool isComplete() const override;
    347351
    348         const StructDecl * aggr() const override { return base; }
    349 
    350         const Type * accept( Visitor & v ) const override { return v.visit( this ); }
    351 private:
    352         StructInstType * clone() const override { return new StructInstType{ *this }; }
    353         MUTATE_FRIEND
    354 };
    355 
    356 /// instance of union type
    357 class UnionInstType final : public ReferenceToType {
    358 public:
    359         readonly<UnionDecl> base;
    360 
    361         UnionInstType( const std::string& n, CV::Qualifiers q = {},
    362                 std::vector<ptr<Attribute>> && as = {} )
    363         : ReferenceToType( n, q, std::move(as) ), base() {}
    364         UnionInstType( const UnionDecl * b, CV::Qualifiers q = {},
    365                 std::vector<ptr<Attribute>> && as = {} );
    366 
    367         bool isComplete() const override;
    368 
    369         const UnionDecl * aggr() const override { return base; }
    370 
    371         const Type * accept( Visitor & v ) const override { return v.visit( this ); }
    372 private:
    373         UnionInstType * clone() const override { return new UnionInstType{ *this }; }
    374         MUTATE_FRIEND
    375 };
    376 
    377 /// instance of enum type
    378 class EnumInstType final : public ReferenceToType {
    379 public:
    380         readonly<EnumDecl> base;
    381 
    382         EnumInstType( const std::string& n, CV::Qualifiers q = {},
    383                 std::vector<ptr<Attribute>> && as = {} )
    384         : ReferenceToType( n, q, std::move(as) ), base() {}
    385         EnumInstType( const EnumDecl * b, CV::Qualifiers q = {},
    386                 std::vector<ptr<Attribute>> && as = {} );
    387 
    388         bool isComplete() const override;
    389 
    390         const EnumDecl * aggr() const override { return base; }
    391 
    392         const Type * accept( Visitor & v ) const override { return v.visit( this ); }
    393 private:
    394         EnumInstType * clone() const override { return new EnumInstType{ *this }; }
    395         MUTATE_FRIEND
    396 };
    397 
    398 /// instance of trait type
    399 class TraitInstType final : public ReferenceToType {
     352        const decl_t * aggr() const override { return base; }
     353
     354        const Type * accept( Visitor & v ) const override { return v.visit( this ); }
     355private:
     356        SueInstType<decl_t> * clone() const override { return new SueInstType<decl_t>{ *this }; }
     357        MUTATE_FRIEND
     358};
     359
     360/// An instance of a struct type.
     361using StructInstType = SueInstType<StructDecl>;
     362
     363/// An instance of a union type.
     364using UnionInstType = SueInstType<UnionDecl>;
     365
     366/// An instance of an enum type.
     367using EnumInstType = SueInstType<EnumDecl>;
     368
     369/// An instance of a trait type.
     370class TraitInstType final : public BaseInstType {
    400371public:
    401372        readonly<TraitDecl> base;
    402373
    403         TraitInstType( const std::string& n, CV::Qualifiers q = {},
    404                 std::vector<ptr<Attribute>> && as = {} )
    405         : ReferenceToType( n, q, std::move(as) ), base() {}
    406         TraitInstType( const TraitDecl * b, CV::Qualifiers q = {},
    407                 std::vector<ptr<Attribute>> && as = {} );
     374        TraitInstType(
     375                const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
     376        : BaseInstType( n, q, std::move(as) ), base() {}
     377
     378        TraitInstType(
     379                const TraitDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
    408380
    409381        // not meaningful for TraitInstType
     
    419391
    420392/// instance of named type alias (typedef or variable)
    421 class TypeInstType final : public ReferenceToType {
     393class TypeInstType final : public BaseInstType {
    422394public:
    423395        readonly<TypeDecl> base;
     396        // previously from renameTyVars; now directly use integer fields instead of synthesized strings
     397        // a nonzero value of formal_usage indicates a formal type (only used in function type)
     398        // a zero value of formal_usage indicates an actual type (referenced inside body of parametric structs and functions)
    424399        TypeDecl::Kind kind;
    425 
    426         TypeInstType( const std::string& n, const TypeDecl * b, CV::Qualifiers q = {},
     400        int formal_usage = 0;
     401        int expr_id = 0;
     402
     403        // compact representation used for map lookups.
     404        struct TypeEnvKey {
     405                const TypeDecl * base;
     406                int formal_usage;
     407                int expr_id;
     408
     409                TypeEnvKey() = default;
     410                TypeEnvKey(const TypeDecl * base, int formal_usage = 0, int expr_id = 0): base(base), formal_usage(formal_usage), expr_id(expr_id) {}
     411                TypeEnvKey(const TypeInstType & inst): base(inst.base), formal_usage(inst.formal_usage), expr_id(inst.expr_id) {}
     412                std::string typeString() const { return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + base->name; }
     413                bool operator==(const TypeEnvKey & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; }
     414
     415        };
     416
     417        bool operator==(const TypeInstType & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; }
     418
     419        TypeInstType(
     420                const std::string& n, const TypeDecl * b, CV::Qualifiers q = {},
    427421                std::vector<ptr<Attribute>> && as = {} )
    428         : ReferenceToType( n, q, std::move(as) ), base( b ), kind( b->kind ) {}
     422        : BaseInstType( n, q, std::move(as) ), base( b ), kind( b->kind ) {}
    429423        TypeInstType( const std::string& n, TypeDecl::Kind k, CV::Qualifiers q = {},
    430424                std::vector<ptr<Attribute>> && as = {} )
    431         : ReferenceToType( n, q, std::move(as) ), base(), kind( k ) {}
     425        : BaseInstType( n, q, std::move(as) ), base(), kind( k ) {}
     426
     427        TypeInstType( const TypeInstType & o ) = default;
     428
     429        TypeInstType( const TypeEnvKey & key )
     430        : BaseInstType(key.base->name), base(key.base), kind(key.base->kind), formal_usage(key.formal_usage), expr_id(key.expr_id) {}
    432431
    433432        /// sets `base`, updating `kind` correctly
     
    440439
    441440        const Type * accept( Visitor & v ) const override { return v.visit( this ); }
     441
     442        std::string typeString() const {
     443                if (formal_usage > 0) return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + name;
     444                else return name;
     445        }
    442446private:
    443447        TypeInstType * clone() const override { return new TypeInstType{ *this }; }
     
    531535};
    532536
     537bool isUnboundType(const Type * type);
     538
     539}
     540
     541namespace std {
     542        template<>
     543        struct hash<typename ast::TypeInstType::TypeEnvKey> {
     544                size_t operator() (const ast::TypeInstType::TypeEnvKey & x) const {
     545                        const size_t p = 1000007;
     546                        size_t res = reinterpret_cast<size_t>(x.base);
     547                        res = p * res + x.formal_usage;
     548                        res = p * res + x.expr_id;
     549                        return res;
     550                }
     551        };
    533552}
    534553
  • src/AST/TypeEnvironment.cpp

    r3c64c668 r58fe85a  
    3434#include "ResolvExpr/Unify.h"      // for unifyInexact
    3535#include "Tuples/Tuples.h"         // for isTtype
     36#include "CompilationState.h"
    3637
    3738using ResolvExpr::WidenMode;
     
    5152        for ( const auto & i : open ) {
    5253                if ( first ) { first = false; } else { out << ' '; }
    53                 out << i.first << "(" << i.second << ")";
     54                out << i.first.typeString() << "(" << i.second << ")";
    5455        }
    5556}
    5657
    5758void print( std::ostream & out, const EqvClass & clz, Indenter indent ) {
    58         out << "( ";
    59         std::copy( clz.vars.begin(), clz.vars.end(), std::ostream_iterator< std::string >( out, " " ) );
     59        out << "(";
     60        bool first = true;
     61        for(const auto & var : clz.vars) {
     62                if(first) first = false;
     63                else out << " ";
     64
     65                if( deterministic_output ) out << "[unbound]";
     66                else out << "_" << var.formal_usage << "_" << var.expr_id << "_";
     67
     68                out << var.base->name;
     69        }
    6070        out << ")";
    61        
     71
    6272        if ( clz.bound ) {
    6373                out << " -> ";
     
    7282}
    7383
    74 const EqvClass * TypeEnvironment::lookup( const std::string & var ) const {
     84const EqvClass * TypeEnvironment::lookup( const TypeInstType::TypeEnvKey & var ) const {
    7585        for ( ClassList::const_iterator i = env.begin(); i != env.end(); ++i ) {
    7686                if ( i->vars.find( var ) != i->vars.end() ) return &*i;
     
    92102                                }
    93103                        }
    94                        
     104
    95105                        i = next;  // go to next node even if this removed
    96106                }
     
    98108}
    99109
    100 void TypeEnvironment::add( const ParameterizedType::ForallList & tyDecls ) {
    101         for ( const TypeDecl * tyDecl : tyDecls ) {
     110void TypeEnvironment::add( const FunctionType::ForallList & tyDecls ) {
     111        for ( auto & tyDecl : tyDecls ) {
    102112                env.emplace_back( tyDecl );
    103113        }
     
    112122void TypeEnvironment::writeToSubstitution( TypeSubstitution & sub ) const {
    113123        for ( const auto & clz : env ) {
    114                 std::string clzRep;
     124                TypeInstType::TypeEnvKey clzRep;
     125                bool first = true;
    115126                for ( const auto & var : clz.vars ) {
    116127                        if ( clz.bound ) {
    117128                                sub.add( var, clz.bound );
    118                         } else if ( clzRep.empty() ) {
     129                        } else if ( first ) {
    119130                                clzRep = var;
     131                                first = false;
    120132                        } else {
    121                                 sub.add( var, new TypeInstType{ clzRep, clz.data.kind } );
     133                                sub.add( var, new TypeInstType{ clzRep } );
    122134                        }
    123135                }
     
    134146        struct Occurs : public ast::WithVisitorRef<Occurs> {
    135147                bool result;
    136                 std::set< std::string > vars;
     148                std::unordered_set< TypeInstType::TypeEnvKey > vars;
    137149                const TypeEnvironment & tenv;
    138150
    139                 Occurs( const std::string & var, const TypeEnvironment & env )
     151                Occurs( const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env )
    140152                : result( false ), vars(), tenv( env ) {
    141153                        if ( const EqvClass * clz = tenv.lookup( var ) ) {
     
    147159
    148160                void previsit( const TypeInstType * typeInst ) {
    149                         if ( vars.count( typeInst->name ) ) {
     161                        if ( vars.count( *typeInst ) ) {
    150162                                result = true;
    151                         } else if ( const EqvClass * clz = tenv.lookup( typeInst->name ) ) {
     163                        } else if ( const EqvClass * clz = tenv.lookup( *typeInst ) ) {
    152164                                if ( clz->bound ) {
    153165                                        clz->bound->accept( *visitor );
     
    158170
    159171        /// true if `var` occurs in `ty` under `env`
    160         bool occurs( const Type * ty, const std::string & var, const TypeEnvironment & env ) {
     172        bool occurs( const Type * ty, const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env ) {
    161173                Pass<Occurs> occur{ var, env };
    162174                maybe_accept( ty, occur );
    163                 return occur.pass.result;
    164         }
    165 }
    166 
    167 bool TypeEnvironment::combine( 
     175                return occur.core.result;
     176        }
     177}
     178
     179bool TypeEnvironment::combine(
    168180                const TypeEnvironment & o, OpenVarSet & open, const SymbolTable & symtab ) {
    169181        // short-circuit easy cases
     
    199211                                auto st = internal_lookup( *vt );
    200212                                if ( st == env.end() ) {
    201                                         // unbound, safe to add if occurs 
     213                                        // unbound, safe to add if occurs
    202214                                        if ( r.bound && occurs( r.bound, *vt, *this ) ) return false;
    203215                                        r.vars.emplace( *vt );
     
    266278}
    267279
    268 bool TypeEnvironment::bindVar( 
    269                 const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data, 
    270                 AssertionSet & need, AssertionSet & have, const OpenVarSet & open, WidenMode widen, 
    271                 const SymbolTable & symtab 
     280bool TypeEnvironment::bindVar(
     281                const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data,
     282                AssertionSet & need, AssertionSet & have, const OpenVarSet & open, WidenMode widen,
     283                const SymbolTable & symtab
    272284) {
    273285        // remove references from bound type, so that type variables can only bind to value types
    274286        ptr<Type> target = bindTo->stripReferences();
    275         auto tyvar = open.find( typeInst->name );
     287        auto tyvar = open.find( *typeInst );
    276288        assert( tyvar != open.end() );
    277289        if ( ! tyVarCompatible( tyvar->second, target ) ) return false;
    278         if ( occurs( target, typeInst->name, *this ) ) return false;
    279 
    280         auto it = internal_lookup( typeInst->name );
     290        if ( occurs( target, *typeInst, *this ) ) return false;
     291
     292        auto it = internal_lookup( *typeInst );
    281293        if ( it != env.end() ) {
    282294                if ( it->bound ) {
     
    286298                        ptr<Type> newType = it->bound;
    287299                        reset_qualifiers( newType, typeInst->qualifiers );
    288                         if ( unifyInexact( 
    289                                         newType, target, *this, need, have, open, 
     300                        if ( unifyInexact(
     301                                        newType, target, *this, need, have, open,
    290302                                        widen & WidenMode{ it->allowWidening, true }, symtab, common ) ) {
    291303                                if ( common ) {
     
    300312                }
    301313        } else {
    302                 env.emplace_back( 
    303                         typeInst->name, target, widen.first && widen.second, data );
     314                env.emplace_back(
     315                        *typeInst, target, widen.first && widen.second, data );
    304316        }
    305317        return true;
    306318}
    307319
    308 bool TypeEnvironment::bindVarToVar( 
    309                 const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data, 
    310                 AssertionSet & need, AssertionSet & have, const OpenVarSet & open, 
    311                 WidenMode widen, const SymbolTable & symtab 
     320bool TypeEnvironment::bindVarToVar(
     321                const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data,
     322                AssertionSet & need, AssertionSet & have, const OpenVarSet & open,
     323                WidenMode widen, const SymbolTable & symtab
    312324) {
    313         auto c1 = internal_lookup( var1->name );
    314         auto c2 = internal_lookup( var2->name );
    315        
     325        auto c1 = internal_lookup( *var1 );
     326        auto c2 = internal_lookup( *var2 );
     327
    316328        // exit early if variables already bound together
    317329        if ( c1 != env.end() && c1 == c2 ) {
     
    326338        if ( c1 != env.end() ) {
    327339                if ( c1->bound ) {
    328                         if ( occurs( c1->bound, var2->name, *this ) ) return false;
     340                        if ( occurs( c1->bound, *var2, *this ) ) return false;
    329341                        type1 = c1->bound;
    330342                }
     
    333345        if ( c2 != env.end() ) {
    334346                if ( c2->bound ) {
    335                         if ( occurs( c2->bound, var1->name, *this ) ) return false;
     347                        if ( occurs( c2->bound, *var1, *this ) ) return false;
    336348                        type2 = c2->bound;
    337349                }
     
    371383        } else if ( c1 != env.end() ) {
    372384                // var2 unbound, add to env[c1]
    373                 c1->vars.emplace( var2->name );
     385                c1->vars.emplace( *var2 );
    374386                c1->allowWidening = widen1;
    375387                c1->data.isComplete |= data.isComplete;
    376388        } else if ( c2 != env.end() ) {
    377389                // var1 unbound, add to env[c2]
    378                 c2->vars.emplace( var1->name );
     390                c2->vars.emplace( *var1 );
    379391                c2->allowWidening = widen2;
    380392                c2->data.isComplete |= data.isComplete;
    381393        } else {
    382394                // neither var bound, create new class
    383                 env.emplace_back( var1->name, var2->name, widen1 && widen2, data );
     395                env.emplace_back( *var1, *var2, widen1 && widen2, data );
    384396        }
    385397
     
    396408}
    397409
    398 bool TypeEnvironment::mergeBound( 
     410bool TypeEnvironment::mergeBound(
    399411                EqvClass & to, const EqvClass & from, OpenVarSet & open, const SymbolTable & symtab ) {
    400412        if ( from.bound ) {
     
    406418                        AssertionSet need, have;
    407419
    408                         if ( unifyInexact( 
     420                        if ( unifyInexact(
    409421                                        toType, fromType, *this, need, have, open, widen, symtab, common ) ) {
    410422                                // unifies, set common type if necessary
     
    424436}
    425437
    426 bool TypeEnvironment::mergeClasses( 
     438bool TypeEnvironment::mergeClasses(
    427439        ClassList::iterator to, ClassList::iterator from, OpenVarSet & open, const SymbolTable & symtab
    428440) {
     
    445457}
    446458
    447 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const std::string & var ) {
     459TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const TypeInstType::TypeEnvKey & var ) {
    448460        for ( ClassList::iterator i = env.begin(); i != env.end(); ++i ) {
    449461                if ( i->vars.count( var ) ) return i;
  • src/AST/TypeEnvironment.hpp

    r3c64c668 r58fe85a  
    3737/// Adding this comparison operator significantly improves assertion satisfaction run time for
    3838/// some cases. The current satisfaction algorithm's speed partially depends on the order of
    39 /// assertions. Assertions which have fewer possible matches should appear before assertions 
    40 /// which have more possible matches. This seems to imply that this could be further improved 
    41 /// by providing an indexer as an additional argument and ordering based on the number of 
     39/// assertions. Assertions which have fewer possible matches should appear before assertions
     40/// which have more possible matches. This seems to imply that this could be further improved
     41/// by providing an indexer as an additional argument and ordering based on the number of
    4242/// matches of the same kind (object, function) for the names of the declarations.
    4343///
    44 /// I've seen a TU go from 54 minutes to 1 minute 34 seconds with the addition of this 
     44/// I've seen a TU go from 54 minutes to 1 minute 34 seconds with the addition of this
    4545/// comparator.
    4646///
    47 /// Note: since this compares pointers for position, minor changes in the source file that 
    48 /// affect memory layout can alter compilation time in unpredictable ways. For example, the 
    49 /// placement of a line directive can reorder type pointers with respect to each other so that 
    50 /// assertions are seen in different orders, causing a potentially different number of 
    51 /// unification calls when resolving assertions. I've seen a TU go from 36 seconds to 27 
    52 /// seconds by reordering line directives alone, so it would be nice to fix this comparison so 
    53 /// that assertions compare more consistently. I've tried to modify this to compare on mangle 
    54 /// name instead of type as the second comparator, but this causes some assertions to never be 
     47/// Note: since this compares pointers for position, minor changes in the source file that
     48/// affect memory layout can alter compilation time in unpredictable ways. For example, the
     49/// placement of a line directive can reorder type pointers with respect to each other so that
     50/// assertions are seen in different orders, causing a potentially different number of
     51/// unification calls when resolving assertions. I've seen a TU go from 36 seconds to 27
     52/// seconds by reordering line directives alone, so it would be nice to fix this comparison so
     53/// that assertions compare more consistently. I've tried to modify this to compare on mangle
     54/// name instead of type as the second comparator, but this causes some assertions to never be
    5555/// recorded. More investigation is needed.
    5656struct AssertCompare {
    57         bool operator()( const DeclWithType * d1, const DeclWithType * d2 ) const {
    58                 int cmp = d1->name.compare( d2->name );
    59                 return cmp < 0 || ( cmp == 0 && d1->get_type() < d2->get_type() );
     57        bool operator()( const VariableExpr * d1, const VariableExpr * d2 ) const {
     58                int cmp = d1->var->name.compare( d2->var->name );
     59                return cmp < 0 || ( cmp == 0 && d1->result < d2->result );
    6060        }
    6161};
     
    7070
    7171/// Set of assertions pending satisfaction
    72 using AssertionSet = std::map< readonly<DeclWithType>, AssertionSetValue, AssertCompare >;
     72using AssertionSet = std::map< const VariableExpr *, AssertionSetValue, AssertCompare >;
    7373
    7474/// Set of open variables
    75 using OpenVarSet = std::unordered_map< std::string, TypeDecl::Data >;
     75using OpenVarSet = std::unordered_map< TypeInstType::TypeEnvKey, TypeDecl::Data >;
    7676
    7777/// Merges one set of open vars into another
     
    8686void print( std::ostream &, const OpenVarSet &, Indenter indent = {} );
    8787
    88 /// Represents an equivalence class of bound type variables, optionally with the concrete type 
     88/// Represents an equivalence class of bound type variables, optionally with the concrete type
    8989/// they bind to.
    9090struct EqvClass {
    91         std::set< std::string > vars;
     91        std::unordered_set< TypeInstType::TypeEnvKey > vars;
    9292        ptr<Type> bound;
    9393        bool allowWidening;
     
    9595
    9696        EqvClass() : vars(), bound(), allowWidening( true ), data() {}
    97        
     97
    9898        /// Copy-with-bound constructor
    99         EqvClass( const EqvClass & o, const Type * b ) 
     99        EqvClass( const EqvClass & o, const Type * b )
    100100        : vars( o.vars ), bound( b ), allowWidening( o.allowWidening ), data( o.data ) {}
    101101
    102102        /// Singleton class constructor from TypeDecl
    103         EqvClass( const TypeDecl * decl )
    104         : vars{ decl->name }, bound(), allowWidening( true ), data( decl ) {}
     103        EqvClass( const TypeInstType * inst )
     104        : vars{ *inst }, bound(), allowWidening( true ), data( inst->base ) {}
    105105
    106106        /// Singleton class constructor from substitution
    107         EqvClass( const std::string & v, const Type * b )
     107        EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b )
    108108        : vars{ v }, bound( b ), allowWidening( false ), data( TypeDecl::Dtype, false ) {}
    109109
    110110        /// Single-var constructor (strips qualifiers from bound type)
    111         EqvClass( const std::string & v, const Type * b, bool w, const TypeDecl::Data & d )
     111        EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b, bool w, const TypeDecl::Data & d )
    112112        : vars{ v }, bound( b ), allowWidening( w ), data( d ) {
    113113                reset_qualifiers( bound );
     
    115115
    116116        /// Double-var constructor
    117         EqvClass( const std::string & v, const std::string & u, bool w, const TypeDecl::Data & d )
     117        EqvClass( const TypeInstType::TypeEnvKey & v, const TypeInstType::TypeEnvKey & u, bool w, const TypeDecl::Data & d )
    118118        : vars{ v, u }, bound(), allowWidening( w ), data( d ) {}
    119119
     
    131131public:
    132132        /// Finds the equivalence class containing a variable; nullptr for none such
    133         const EqvClass * lookup( const std::string & var ) const;
     133        const EqvClass * lookup( const TypeInstType::TypeEnvKey & var ) const;
    134134
    135135        /// Add a new equivalence class for each type variable
    136         void add( const ParameterizedType::ForallList & tyDecls );
     136        void add( const FunctionType::ForallList & tyDecls );
    137137
    138138        /// Add a new equivalence class for each branch of the substitution, checking for conflicts
     
    142142        void writeToSubstitution( TypeSubstitution & sub ) const;
    143143
    144         template< typename node_t, enum Node::ref_type ref_t >
    145         int apply( ptr_base< node_t, ref_t > & type ) const {
     144        template< typename node_t >
     145        auto apply( node_t && type ) const {
    146146                TypeSubstitution sub;
    147147                writeToSubstitution( sub );
    148                 return sub.apply( type );
    149         }
    150 
    151         template< typename node_t, enum Node::ref_type ref_t >
    152         int applyFree( ptr_base< node_t, ref_t > & type ) const {
     148                return sub.apply( std::forward<node_t>(type) );
     149        }
     150
     151        template< typename node_t >
     152        auto applyFree( node_t && type ) const {
    153153                TypeSubstitution sub;
    154154                writeToSubstitution( sub );
    155                 return sub.applyFree( type );
     155                return sub.applyFree( std::forward<node_t>(type) );
    156156        }
    157157
     
    172172        void addActual( const TypeEnvironment & actualEnv, OpenVarSet & openVars );
    173173
    174         /// Binds the type class represented by `typeInst` to the type `bindTo`; will add the class if 
     174        /// Binds the type class represented by `typeInst` to the type `bindTo`; will add the class if
    175175        /// needed. Returns false on failure.
    176         bool bindVar( 
    177                 const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data, 
    178                 AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 
     176        bool bindVar(
     177                const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data,
     178                AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars,
    179179                ResolvExpr::WidenMode widen, const SymbolTable & symtab );
    180        
    181         /// Binds the type classes represented by `var1` and `var2` together; will add one or both 
     180
     181        /// Binds the type classes represented by `var1` and `var2` together; will add one or both
    182182        /// classes if needed. Returns false on failure.
    183         bool bindVarToVar( 
    184                 const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data, 
    185                 AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 
     183        bool bindVarToVar(
     184                const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data,
     185                AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars,
    186186                ResolvExpr::WidenMode widen, const SymbolTable & symtab );
    187187
     
    198198
    199199        /// Unifies the type bound of `to` with the type bound of `from`, returning false if fails
    200         bool mergeBound( 
     200        bool mergeBound(
    201201                EqvClass & to, const EqvClass & from, OpenVarSet & openVars, const SymbolTable & symtab );
    202202
    203203        /// Merges two type classes from local environment, returning false if fails
    204         bool mergeClasses( 
    205                 ClassList::iterator to, ClassList::iterator from, OpenVarSet & openVars, 
     204        bool mergeClasses(
     205                ClassList::iterator to, ClassList::iterator from, OpenVarSet & openVars,
    206206                const SymbolTable & symtab );
    207207
    208208        /// Private lookup API; returns array index of string, or env.size() for not found
    209         ClassList::iterator internal_lookup( const std::string & );
     209        ClassList::iterator internal_lookup( const TypeInstType::TypeEnvKey & );
    210210};
    211211
  • src/AST/TypeSubstitution.cpp

    r3c64c668 r58fe85a  
    1919namespace ast {
    2020
     21
     22// size_t TypeSubstitution::Substituter::traceId = Stats::Heap::new_stacktrace_id("TypeSubstitution");
     23
    2124TypeSubstitution::TypeSubstitution() {
    2225}
     
    3639void TypeSubstitution::initialize( const TypeSubstitution &src, TypeSubstitution &dest ) {
    3740        dest.typeEnv.clear();
    38         dest.varEnv.clear();
    3941        dest.add( src );
    4042}
     
    4446                typeEnv[ i->first ] = i->second;
    4547        } // for
    46         for ( VarEnvType::const_iterator i = other.varEnv.begin(); i != other.varEnv.end(); ++i ) {
    47                 varEnv[ i->first ] = i->second;
    48         } // for
    4948}
    5049
    51 void TypeSubstitution::add( std::string formalType, const Type *actualType ) {
    52         typeEnv[ formalType ] = actualType;
     50void TypeSubstitution::add( const TypeInstType * formalType, const Type *actualType ) {
     51        typeEnv[ *formalType ] = actualType;
    5352}
    5453
    55 void TypeSubstitution::addVar( std::string formalExpr, const Expr *actualExpr ) {
    56         varEnv[ formalExpr ] = actualExpr;
     54void TypeSubstitution::add( const TypeInstType::TypeEnvKey & key, const Type * actualType) {
     55        typeEnv[ key ] = actualType;
    5756}
    5857
    59 void TypeSubstitution::remove( std::string formalType ) {
    60         TypeEnvType::iterator i = typeEnv.find( formalType );
     58void TypeSubstitution::remove( const TypeInstType * formalType ) {
     59        TypeEnvType::iterator i = typeEnv.find( *formalType );
    6160        if ( i != typeEnv.end() ) {
    62                 typeEnv.erase( formalType );
     61                typeEnv.erase( *formalType );
    6362        } // if
    6463}
    6564
    66 const Type *TypeSubstitution::lookup( std::string formalType ) const {
    67         TypeEnvType::const_iterator i = typeEnv.find( formalType );
     65const Type *TypeSubstitution::lookup( const TypeInstType * formalType ) const {
     66        TypeEnvType::const_iterator i = typeEnv.find( *formalType );
    6867
    6968        // break on not in substitution set
     
    7271        // attempt to transitively follow TypeInstType links.
    7372        while ( const TypeInstType *actualType = i->second.as<TypeInstType>()) {
    74                 const std::string& typeName = actualType->name;
    75 
    7673                // break cycles in the transitive follow
    77                 if ( formalType == typeName ) break;
     74                if ( *formalType == *actualType ) break;
    7875
    7976                // Look for the type this maps to, returning previous mapping if none-such
    80                 i = typeEnv.find( typeName );
     77                i = typeEnv.find( *actualType );
    8178                if ( i == typeEnv.end() ) return actualType;
    8279        }
     
    8784
    8885bool TypeSubstitution::empty() const {
    89         return typeEnv.empty() && varEnv.empty();
     86        return typeEnv.empty();
    9087}
    9188
    9289namespace {
    9390        struct EnvTrimmer {
    94                 ptr<TypeSubstitution> env;
     91                const TypeSubstitution * env;
    9592                TypeSubstitution * newEnv;
    9693                EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){}
    97                 void previsit( TypeDecl * tyDecl ) {
     94                void previsit( FunctionType * ftype ) {
    9895                        // transfer known bindings for seen type variables
    99                         if ( const Type * t = env->lookup( tyDecl->name ) ) {
    100                                 newEnv->add( tyDecl->name, t );
     96                        for (auto & formal : ftype->forall) {
     97                                if ( const Type * t = env->lookup( formal ) ) {
     98                                        newEnv->add( formal, t );
     99                                }
    101100                        }
    102101                }
     
    108107        if ( env ) {
    109108                TypeSubstitution * newEnv = new TypeSubstitution();
    110 #if TIME_TO_CONVERT_PASSES
    111109                Pass<EnvTrimmer> trimmer( env, newEnv );
    112110                expr->accept( trimmer );
    113 #else
    114                 (void)expr;
    115                 (void)env;
    116 #endif
    117111                return newEnv;
    118112        }
     
    121115
    122116void TypeSubstitution::normalize() {
    123 #if TIME_TO_CONVERT_PASSES
    124         PassVisitor<Substituter> sub( *this, true );
     117        Pass<Substituter> sub( *this, true );
    125118        do {
    126                 sub.pass.subCount = 0;
    127                 sub.pass.freeOnly = true;
     119                sub.core.subCount = 0;
     120                sub.core.freeOnly = true;
    128121                for ( TypeEnvType::iterator i = typeEnv.begin(); i != typeEnv.end(); ++i ) {
    129                         i->second = i->second->acceptMutator( sub );
     122                        i->second = i->second->accept( sub );
    130123                }
    131         } while ( sub.pass.subCount );
    132 #endif
     124        } while ( sub.core.subCount );
    133125}
    134126
    135 #if TIME_TO_CONVERT_PASSES
    136 
    137 Type * TypeSubstitution::Substituter::postmutate( TypeInstType *inst ) {
    138         BoundVarsType::const_iterator bound = boundVars.find( inst->name );
     127const Type * TypeSubstitution::Substituter::postvisit( const TypeInstType *inst ) {
     128        BoundVarsType::const_iterator bound = boundVars.find( *inst );
    139129        if ( bound != boundVars.end() ) return inst;
    140130
    141         TypeEnvType::const_iterator i = sub.typeEnv.find( inst->name );
     131        TypeEnvType::const_iterator i = sub.typeEnv.find( *inst );
    142132        if ( i == sub.typeEnv.end() ) {
    143133                return inst;
     
    146136                // Note: this does not prevent cycles in the general case, so it may be necessary to do something more sophisticated here.
    147137                // TODO: investigate preventing type variables from being bound to themselves in the first place.
    148                 if ( TypeInstType * replacement = i->second.as<TypeInstType>() ) {
    149                         if ( inst->name == replacement->name ) {
     138                if ( const TypeInstType * replacement = i->second.as<TypeInstType>() ) {
     139                        if ( *inst == *replacement ) {
    150140                                return inst;
    151141                        }
     
    153143                // std::cerr << "found " << inst->name << ", replacing with " << i->second << std::endl;
    154144                subCount++;
    155                 Type * newtype = i->second->clone();
    156                 newtype->get_qualifiers() |= inst->get_qualifiers();
    157                 delete inst;
    158                 // Note: need to recursively apply substitution to the new type because normalize does not substitute bound vars, but bound vars must be substituted when not in freeOnly mode.
    159                 return newtype->acceptMutator( *visitor );
     145                ptr<Type> newType = i->second; // force clone if needed
     146                add_qualifiers( newType, inst->qualifiers );
     147                // Note: need to recursively apply substitution to the new type because normalize does not
     148                // substitute bound vars, but bound vars must be substituted when not in freeOnly mode.
     149                newType = newType->accept( *visitor );
     150                return newType.release();
    160151        } // if
    161152}
    162153
    163 Expression * TypeSubstitution::Substituter::postmutate( NameExpr * nameExpr ) {
    164         VarEnvType::const_iterator i = sub.varEnv.find( nameExpr->name );
    165         if ( i == sub.varEnv.end() ) {
    166                 return nameExpr;
    167         } else {
    168                 subCount++;
    169                 delete nameExpr;
    170                 return i->second->clone();
    171         } // if
    172 }
    173 
    174 void TypeSubstitution::Substituter::premutate( Type * type ) {
     154void TypeSubstitution::Substituter::previsit( const FunctionType * ptype ) {
    175155        GuardValue( boundVars );
    176156        // bind type variables from forall-qualifiers
    177157        if ( freeOnly ) {
    178                 for ( Type::ForallList::const_iterator tyvar = type->forall.begin(); tyvar != type->forall.end(); ++tyvar ) {
    179                         boundVars.insert( (*tyvar)->name );
     158                for ( auto & tyvar : ptype->forall ) {
     159                                boundVars.insert( *tyvar );
    180160                } // for
    181161        } // if
    182162}
    183163
    184 template< typename TypeClass >
    185 void TypeSubstitution::Substituter::handleAggregateType( TypeClass * type ) {
     164/*
     165void TypeSubstitution::Substituter::handleAggregateType( const BaseInstType * type ) {
    186166        GuardValue( boundVars );
    187167        // bind type variables from forall-qualifiers
    188168        if ( freeOnly ) {
    189                 for ( Type::ForallList::const_iterator tyvar = type->forall.begin(); tyvar != type->forall.end(); ++tyvar ) {
    190                         boundVars.insert( (*tyvar)->name );
    191                 } // for
    192169                // bind type variables from generic type instantiations
    193                 std::list< TypeDecl* > *baseParameters = type->get_baseParameters();
    194                 if ( baseParameters && ! type->parameters.empty() ) {
    195                         for ( std::list< TypeDecl* >::const_iterator tyvar = baseParameters->begin(); tyvar != baseParameters->end(); ++tyvar ) {
    196                                 boundVars.insert( (*tyvar)->name );
    197                         } // for
    198                 } // if
     170                if ( auto decl = type->aggr() ) {
     171                        if ( ! type->params.empty() ) {
     172                                for ( const TypeDecl * tyvar : decl->params ) {
     173                                        boundVars.insert( *tyvar );
     174                                } // for
     175                        } // if
     176                }
    199177        } // if
    200178}
    201179
    202 void TypeSubstitution::Substituter::premutate( StructInstType * aggregateUseType ) {
     180void TypeSubstitution::Substituter::previsit( const StructInstType * aggregateUseType ) {
    203181        handleAggregateType( aggregateUseType );
    204182}
    205183
    206 void TypeSubstitution::Substituter::premutate( UnionInstType *aggregateUseType ) {
     184void TypeSubstitution::Substituter::previsit( const UnionInstType *aggregateUseType ) {
    207185        handleAggregateType( aggregateUseType );
    208186}
    209 
    210 #endif
     187*/
    211188
    212189} // namespace ast
  • src/AST/TypeSubstitution.hpp

    r3c64c668 r58fe85a  
    4444        TypeSubstitution &operator=( const TypeSubstitution &other );
    4545
    46         template< typename SynTreeClass > int apply( const SynTreeClass *& input ) const;
    47         template< typename SynTreeClass > int applyFree( const SynTreeClass *& input ) const;
     46        template< typename SynTreeClass >
     47        struct ApplyResult {
     48                ast::ptr<SynTreeClass> node;
     49                int count;
     50        };
     51
     52        template< typename SynTreeClass > ApplyResult<SynTreeClass> apply( const SynTreeClass * input ) const;
     53        template< typename SynTreeClass > ApplyResult<SynTreeClass> applyFree( const SynTreeClass * input ) const;
    4854
    4955        template< typename node_t, enum Node::ref_type ref_t >
    5056        int apply( ptr_base< node_t, ref_t > & input ) const {
    5157                const node_t * p = input.get();
    52                 int ret = apply(p);
    53                 input = p;
    54                 return ret;
     58                auto ret = apply(p);
     59                input = ret.node;
     60                return ret.count;
    5561        }
    5662
     
    5864        int applyFree( ptr_base< node_t, ref_t > & input ) const {
    5965                const node_t * p = input.get();
    60                 int ret = applyFree(p);
    61                 input = p;
    62                 return ret;
     66                auto ret = applyFree(p);
     67                input = ret.node;
     68                return ret.count;
    6369        }
    6470
    65         void add( std::string formalType, const Type *actualType );
     71        void add( const TypeInstType * formalType, const Type *actualType );
     72        void add( const TypeInstType::TypeEnvKey & key, const Type *actualType );
    6673        void add( const TypeSubstitution &other );
    67         void remove( std::string formalType );
    68         const Type *lookup( std::string formalType ) const;
     74        void remove( const TypeInstType * formalType );
     75        const Type *lookup( const TypeInstType * formalType ) const;
    6976        bool empty() const;
    70 
    71         void addVar( std::string formalExpr, const Expr *actualExpr );
    7277
    7378        template< typename FormalIterator, typename ActualIterator >
     
    9297        void initialize( const TypeSubstitution &src, TypeSubstitution &dest );
    9398
    94         template<typename pass_type>
     99        template<typename core_t>
    95100        friend class Pass;
    96101
    97         typedef std::unordered_map< std::string, ptr<Type> > TypeEnvType;
    98         typedef std::unordered_map< std::string, ptr<Expr> > VarEnvType;
     102        typedef std::unordered_map< TypeInstType::TypeEnvKey, ptr<Type> > TypeEnvType;
    99103        TypeEnvType typeEnv;
    100         VarEnvType varEnv;
    101104
    102105  public:
     
    107110        auto   end() const -> decltype( typeEnv.  end() ) { return typeEnv.  end(); }
    108111
    109         auto beginVar()       -> decltype( varEnv.begin() ) { return varEnv.begin(); }
    110         auto   endVar()       -> decltype( varEnv.  end() ) { return varEnv.  end(); }
    111         auto beginVar() const -> decltype( varEnv.begin() ) { return varEnv.begin(); }
    112         auto   endVar() const -> decltype( varEnv.  end() ) { return varEnv.  end(); }
    113112};
    114113
     114// this is the only place where type parameters outside a function formal may be substituted.
    115115template< typename FormalIterator, typename ActualIterator >
    116116void TypeSubstitution::add( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) {
     
    123123                        if ( const TypeExpr *actual = actualIt->template as<TypeExpr>() ) {
    124124                                if ( formal->name != "" ) {
    125                                         typeEnv[ formal->name ] = actual->type;
     125                                        typeEnv[ formal ] = actual->type;
    126126                                } // if
    127127                        } else {
     
    129129                        } // if
    130130                } else {
    131                         // TODO: type check the formal and actual parameters
    132                         if ( (*formalIt)->name != "" ) {
    133                                 varEnv[ (*formalIt)->name ] = *actualIt;
    134                         } // if
     131                       
    135132                } // if
    136133        } // for
    137134}
     135
     136
    138137
    139138template< typename FormalIterator, typename ActualIterator >
     
    142141}
    143142
     143
    144144} // namespace ast
    145145
     
    147147// PassVisitor are defined before PassVisitor implementation accesses TypeSubstitution internals.
    148148#include "Pass.hpp"
     149#include "Copy.hpp"
    149150
    150151namespace ast {
    151152
    152153// definitition must happen after PassVisitor is included so that WithGuards can be used
    153 struct TypeSubstitution::Substituter : public WithGuards, public WithVisitorRef<Substituter> {
     154struct TypeSubstitution::Substituter : public WithGuards, public WithVisitorRef<Substituter>, public PureVisitor {
     155                static size_t traceId;
    154156
    155157                Substituter( const TypeSubstitution & sub, bool freeOnly ) : sub( sub ), freeOnly( freeOnly ) {}
    156158
    157 #if TIME_TO_CONVERT_PASSES
    158 
    159                 Type * postmutate( TypeInstType * aggregateUseType );
    160                 Expression * postmutate( NameExpr * nameExpr );
     159                const Type * postvisit( const TypeInstType * aggregateUseType );
    161160
    162161                /// Records type variable bindings from forall-statements
    163                 void premutate( Type * type );
     162                void previsit( const FunctionType * type );
    164163                /// Records type variable bindings from forall-statements and instantiations of generic types
    165                 template< typename TypeClass > void handleAggregateType( TypeClass * type );
     164                // void handleAggregateType( const BaseInstType * type );
    166165
    167                 void premutate( StructInstType * aggregateUseType );
    168                 void premutate( UnionInstType * aggregateUseType );
    169 
    170 #endif
     166                // void previsit( const StructInstType * aggregateUseType );
     167                // void previsit( const UnionInstType * aggregateUseType );
    171168
    172169                const TypeSubstitution & sub;
    173170                int subCount = 0;
    174171                bool freeOnly;
    175                 typedef std::unordered_set< std::string > BoundVarsType;
     172                typedef std::unordered_set< TypeInstType::TypeEnvKey > BoundVarsType;
    176173                BoundVarsType boundVars;
    177174
     
    179176
    180177template< typename SynTreeClass >
    181 int TypeSubstitution::apply( const SynTreeClass *& input ) const {
     178TypeSubstitution::ApplyResult<SynTreeClass> TypeSubstitution::apply( const SynTreeClass * input ) const {
    182179        assert( input );
    183180        Pass<Substituter> sub( *this, false );
    184181        input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) );
    185 ///     std::cerr << "substitution result is: ";
    186 ///     newType->print( std::cerr );
    187 ///     std::cerr << std::endl;
    188         return sub.pass.subCount;
     182        return { input, sub.core.subCount };
    189183}
    190184
    191185template< typename SynTreeClass >
    192 int TypeSubstitution::applyFree( const SynTreeClass *& input ) const {
     186TypeSubstitution::ApplyResult<SynTreeClass> TypeSubstitution::applyFree( const SynTreeClass * input ) const {
    193187        assert( input );
    194188        Pass<Substituter> sub( *this, true );
    195189        input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) );
    196 ///     std::cerr << "substitution result is: ";
    197 ///     newType->print( std::cerr );
    198 ///     std::cerr << std::endl;
    199         return sub.pass.subCount;
     190        return { input, sub.core.subCount };
    200191}
    201192
  • src/AST/Visitor.hpp

    r3c64c668 r58fe85a  
    4747    virtual const ast::Stmt *             visit( const ast::CatchStmt            * ) = 0;
    4848    virtual const ast::Stmt *             visit( const ast::FinallyStmt          * ) = 0;
     49    virtual const ast::Stmt *             visit( const ast::SuspendStmt          * ) = 0;
    4950    virtual const ast::Stmt *             visit( const ast::WaitForStmt          * ) = 0;
    5051    virtual const ast::Decl *             visit( const ast::WithStmt             * ) = 0;
  • src/AST/module.mk

    r3c64c668 r58fe85a  
    1717SRC_AST = \
    1818        AST/AssertAcyclic.cpp \
     19        AST/AssertAcyclic.hpp \
    1920        AST/Attribute.cpp \
     21        AST/Attribute.hpp \
     22        AST/Bitfield.hpp \
     23        AST/Chain.hpp \
    2024        AST/Convert.cpp \
     25        AST/Convert.hpp \
     26        AST/Copy.hpp \
     27        AST/CVQualifiers.hpp \
    2128        AST/Decl.cpp \
     29        AST/Decl.hpp \
    2230        AST/DeclReplacer.cpp \
     31        AST/DeclReplacer.hpp \
     32        AST/Eval.hpp \
    2333        AST/Expr.cpp \
     34        AST/Expr.hpp \
     35        AST/FunctionSpec.hpp \
     36        AST/Fwd.hpp \
    2437        AST/GenericSubstitution.cpp \
     38        AST/GenericSubstitution.hpp \
    2539        AST/Init.cpp \
     40        AST/Init.hpp \
     41        AST/Label.hpp \
    2642        AST/LinkageSpec.cpp \
     43        AST/LinkageSpec.hpp \
    2744        AST/Node.cpp \
     45        AST/Node.hpp \
     46        AST/ParseNode.hpp \
    2847        AST/Pass.cpp \
     48        AST/Pass.hpp \
     49        AST/Pass.impl.hpp \
     50        AST/Pass.proto.hpp \
    2951        AST/Print.cpp \
     52        AST/Print.hpp \
    3053        AST/Stmt.cpp \
     54        AST/Stmt.hpp \
     55        AST/StorageClasses.hpp \
    3156        AST/SymbolTable.cpp \
     57        AST/SymbolTable.hpp \
     58        AST/TranslationUnit.hpp \
    3259        AST/Type.cpp \
     60        AST/Type.hpp \
    3361        AST/TypeEnvironment.cpp \
    34         AST/TypeSubstitution.cpp
     62        AST/TypeEnvironment.hpp \
     63        AST/TypeSubstitution.cpp \
     64        AST/TypeSubstitution.hpp \
     65        AST/Visitor.hpp
    3566
    3667SRC += $(SRC_AST)
  • src/AST/porting.md

    r3c64c668 r58fe85a  
    3030  * Base nodes now override `const Node * accept( Visitor & v ) const = 0` with, e.g. `const Stmt * accept( Visitor & v ) const override = 0`
    3131* `PassVisitor` is replaced with `ast::Pass`
     32  * Most one shot uses can use `ast::Pass::run` and `ast::Pass::read`.
     33
     34`WithConstTypeSubstitution`
     35* `env` => `typeSubs`
    3236
    3337## Structural Changes ##
     
    4751      template<typename node_t>
    4852      friend node_t * mutate(const node_t * node);
     53      template<typename node_t>
     54      friend node_t * shallowCopy(const node_t * node);
     55    or equilant.
     56* You should use the `mutate` function where possible as it avoids extra copies.
     57  * If you must copy use `shallowCopy` or `deepCopy` as required.
    4958
    5059All leaves of the `Node` inheritance tree are now declared `final`
     
    141150  * allows `newObject` as just default settings
    142151
     152`FunctionDecl`
     153* `params` and `returns` added.
     154  * Contain the declarations of the parameters and return variables.
     155  * Types should match (even be shared with) the fields of `type`.
     156
    143157`NamedTypeDecl`
    144158* `parameters` => `params`
     
    149163`AggregateDecl`
    150164* `parameters` => `params`
     165
     166`StructDecl`
     167* `makeInst` replaced by better constructor on `StructInstType`.
    151168
    152169`Expr`
     
    240257* **TODO** move `kind`, `typeNames` into code generator
    241258
    242 `ReferenceToType`
     259`ReferenceToType` => `BaseInstType`
    243260* deleted `get_baseParameters()` from children
    244261  * replace with `aggr() ? aggr()->params : nullptr`
     
    256273* `returnVals` => `returns`
    257274* `parameters` => `params`
     275  * Both now just point at types.
    258276* `bool isVarArgs;` => `enum ArgumentFlag { FixedArgs, VariableArgs }; ArgumentFlag isVarArgs;`
     277
     278`SueInstType`
     279* Template class, with specializations and using to implement some other types:
     280  * `StructInstType`, `UnionInstType` & `EnumInstType`
    259281
    260282`TypeInstType`
  • src/CodeGen/CodeGenerator.cc

    r3c64c668 r58fe85a  
    120120                // GCC builtins should always be printed unmangled
    121121                if ( options.pretty || decl->linkage.is_gcc_builtin ) return decl->name;
    122                 if ( decl->mangleName != "" ) {
     122                if ( LinkageSpec::isMangled(decl->linkage) && decl->mangleName != "" ) {
    123123                        // need to incorporate scope level in order to differentiate names for destructors
    124124                        return decl->get_scopedMangleName();
  • src/CodeGen/FixMain.cc

    r3c64c668 r58fe85a  
    2626#include "SynTree/Declaration.h"   // for FunctionDecl, operator<<
    2727#include "SynTree/Type.h"          // for FunctionType
     28#include "SymTab/Mangler.h"
    2829
    2930namespace CodeGen {
     
    4748                if( main_signature ) {
    4849                        os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return ";
     50                        main_signature->mangleName = SymTab::Mangler::mangle(main_signature.get());
    4951
    5052                        os << main_signature->get_scopedMangleName() << "(";
  • src/CodeGen/FixNames.cc

    r3c64c668 r58fe85a  
    3131#include "SynTree/Type.h"          // for Type, BasicType, Type::Qualifiers
    3232#include "SynTree/Visitor.h"       // for Visitor, acceptAll
     33#include "CompilationState.h"
    3334
    3435namespace CodeGen {
     
    102103                if ( dwt->get_name() != "" ) {
    103104                        if ( LinkageSpec::isMangled( dwt->get_linkage() ) ) {
    104                                 dwt->set_mangleName( SymTab::Mangler::mangle( dwt ) );
     105                                if (!useNewAST) {
     106                                        dwt->set_mangleName( SymTab::Mangler::mangle( dwt ) );
     107                                }
    105108                                dwt->set_scopeLevel( scopeLevel );
    106109                        } // if
  • src/CodeGen/module.mk

    r3c64c668 r58fe85a  
    2020SRC_CODEGEN = \
    2121        CodeGen/CodeGenerator.cc \
     22        CodeGen/CodeGenerator.h \
    2223        CodeGen/FixMain.cc \
     24        CodeGen/FixMain.h \
    2325        CodeGen/GenType.cc \
    24         CodeGen/OperatorTable.cc
     26        CodeGen/GenType.h \
     27        CodeGen/OperatorTable.cc \
     28        CodeGen/OperatorTable.h \
     29        CodeGen/Options.h
    2530
    26 SRC += $(SRC_CODEGEN) CodeGen/Generate.cc CodeGen/FixNames.cc
     31SRC += $(SRC_CODEGEN) CodeGen/Generate.cc CodeGen/Generate.h CodeGen/FixNames.cc CodeGen/FixNames.h
    2732SRCDEMANGLE += $(SRC_CODEGEN)
  • src/CodeTools/TrackLoc.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Tues May 2 15:46:00 2017
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Wed May 3 14:43:00 2017
    13 // Update Count     : 0
     12// Last Modified On : Fri Nov 27 18:00:00 2020
     13// Update Count     : 1
    1414//
    1515
     
    2222#include <string>                    // for operator<<, string
    2323#include <typeindex>                 // for type_index
     24#include <vector>                    // for vector
    2425
    2526#include "Common/PassVisitor.h"      // for PassVisitor
     
    3738                CodeLocation *lastNode;
    3839
    39                 std::stack< CodeLocation * > parents;
     40                std::stack< CodeLocation *, std::vector< CodeLocation * > > parents;
    4041        public:
    4142                LocationPrinter(size_t printLevel) :
  • src/CodeTools/module.mk

    r3c64c668 r58fe85a  
    1515###############################################################################
    1616
    17 SRC += CodeTools/DeclStats.cc \
     17SRC += \
     18        CodeTools/DeclStats.cc \
     19        CodeTools/DeclStats.h \
    1820        CodeTools/ResolvProtoDump.cc \
    19         CodeTools/TrackLoc.cc
     21        CodeTools/ResolvProtoDump.h \
     22        CodeTools/TrackLoc.cc \
     23        CodeTools/TrackLoc.h
  • src/Common/CodeLocation.h

    r3c64c668 r58fe85a  
    4242        }
    4343
    44         bool followedBy( CodeLocation const & other, int seperation ) {
     44        bool startsBefore( CodeLocation const & other ) const {
     45                if( filename < other.filename ) return true;
     46                if( filename > other.filename ) return false;
     47
     48                if( first_line < other.first_line ) return true;
     49                if( first_line > other.first_line ) return false;
     50
     51                if( last_line < other.last_line ) return true;
     52                return false;
     53        }
     54
     55        bool followedBy( CodeLocation const & other, int seperation ) const {
    4556                return (first_line + seperation == other.first_line &&
    4657                        filename == other.filename);
    4758        }
    4859
    49         bool operator==( CodeLocation const & other ) {
     60        bool operator==( CodeLocation const & other ) const {
    5061                return followedBy( other, 0 );
    5162        }
    5263
    53         bool operator!=( CodeLocation const & other ) {
     64        bool operator!=( CodeLocation const & other ) const {
    5465                return !(*this == other);
    5566        }
  • src/Common/Eval.cc

    r3c64c668 r58fe85a  
    168168        if (expr) {
    169169                expr->accept(ev);
    170                 return std::make_pair(ev.pass.value, ev.pass.valid);
     170                return std::make_pair(ev.core.value, ev.core.valid);
    171171        } else {
    172172                return std::make_pair(0, false);
  • src/Common/PassVisitor.h

    r3c64c668 r58fe85a  
    110110        virtual void visit( FinallyStmt * finallyStmt ) override final;
    111111        virtual void visit( const FinallyStmt * finallyStmt ) override final;
     112        virtual void visit( SuspendStmt * suspendStmt ) override final;
     113        virtual void visit( const SuspendStmt * suspendStmt ) override final;
    112114        virtual void visit( WaitForStmt * waitforStmt ) override final;
    113115        virtual void visit( const WaitForStmt * waitforStmt ) override final;
     
    276278        virtual Statement * mutate( CatchStmt * catchStmt ) override final;
    277279        virtual Statement * mutate( FinallyStmt * finallyStmt ) override final;
     280        virtual Statement * mutate( SuspendStmt * suspendStmt ) override final;
    278281        virtual Statement * mutate( WaitForStmt * waitforStmt ) override final;
    279282        virtual Declaration * mutate( WithStmt * withStmt ) override final;
     
    351354        virtual TypeSubstitution * mutate( TypeSubstitution * sub ) final;
    352355
     356        bool isInFunction() const {
     357                return inFunction;
     358        }
     359
    353360private:
    354361        bool inFunction = false;
     362        bool atFunctionTop = false;
    355363
    356364        template<typename pass_t> friend void acceptAll( std::list< Declaration* > &decls, PassVisitor< pass_t >& visitor );
     
    523531public:
    524532        PassVisitor<pass_type> * const visitor = nullptr;
     533
     534        bool isInFunction() const {
     535                return visitor->isInFunction();
     536        }
    525537};
    526538
  • src/Common/PassVisitor.impl.h

    r3c64c668 r58fe85a  
    532532                        indexerAddId( &func );
    533533                        maybeAccept_impl( node->type, *this );
    534                         // function body needs to have the same scope as parameters - CompoundStmt will not enter
    535                         // a new scope if inFunction is true
     534                        // First remember that we are now within a function.
    536535                        ValueGuard< bool > oldInFunction( inFunction );
    537536                        inFunction = true;
     537                        // The function body needs to have the same scope as parameters.
     538                        // A CompoundStmt will not enter a new scope if atFunctionTop is true.
     539                        ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
     540                        atFunctionTop = true;
    538541                        maybeAccept_impl( node->statements, *this );
    539542                        maybeAccept_impl( node->attributes, *this );
     
    567570                        indexerAddId( &func );
    568571                        maybeAccept_impl( node->type, *this );
    569                         // function body needs to have the same scope as parameters - CompoundStmt will not enter
    570                         // a new scope if inFunction is true
     572                        // First remember that we are now within a function.
    571573                        ValueGuard< bool > oldInFunction( inFunction );
    572574                        inFunction = true;
     575                        // The function body needs to have the same scope as parameters.
     576                        // A CompoundStmt will not enter a new scope if atFunctionTop is true.
     577                        ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
     578                        atFunctionTop = true;
    573579                        maybeAccept_impl( node->statements, *this );
    574580                        maybeAccept_impl( node->attributes, *this );
     
    601607                        indexerAddId( &func );
    602608                        maybeMutate_impl( node->type, *this );
    603                         // function body needs to have the same scope as parameters - CompoundStmt will not enter
    604                         // a new scope if inFunction is true
     609                        // First remember that we are now within a function.
    605610                        ValueGuard< bool > oldInFunction( inFunction );
    606611                        inFunction = true;
     612                        // The function body needs to have the same scope as parameters.
     613                        // A CompoundStmt will not enter a new scope if atFunctionTop is true.
     614                        ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
     615                        atFunctionTop = true;
    607616                        maybeMutate_impl( node->statements, *this );
    608617                        maybeMutate_impl( node->attributes, *this );
     
    826835        {
    827836                auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    828                 maybeAccept_impl( node->parameters, *this );
    829837                maybeAccept_impl( node->base      , *this );
    830838        }
     
    849857        {
    850858                auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    851                 maybeAccept_impl( node->parameters, *this );
    852859                maybeAccept_impl( node->base      , *this );
    853860        }
     
    871878        {
    872879                auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    873                 maybeMutate_impl( node->parameters, *this );
    874880                maybeMutate_impl( node->base      , *this );
    875881        }
     
    895901        {
    896902                auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    897                 maybeAccept_impl( node->parameters, *this );
    898903                maybeAccept_impl( node->base      , *this );
    899904        }
     
    912917        {
    913918                auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    914                 maybeAccept_impl( node->parameters, *this );
    915919                maybeAccept_impl( node->base      , *this );
    916920        }
     
    929933        {
    930934                auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    931                 maybeMutate_impl( node->parameters, *this );
    932935                maybeMutate_impl( node->base      , *this );
    933936        }
     
    10071010        VISIT_START( node );
    10081011        {
    1009                 // do not enter a new scope if inFunction is true - needs to check old state before the assignment
    1010                 ValueGuard< bool > oldInFunction( inFunction );
    1011                 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
     1012                // Do not enter a new scope if atFunctionTop is true, don't leave one either.
     1013                ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
     1014                auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
    10121015                auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
    1013                 inFunction = false;
     1016                atFunctionTop = false;
    10141017                visitStatementList( node->kids );
    10151018        }
     
    10211024        VISIT_START( node );
    10221025        {
    1023                 // do not enter a new scope if inFunction is true - needs to check old state before the assignment
    1024                 ValueGuard< bool > oldInFunction( inFunction );
    1025                 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
     1026                // Do not enter a new scope if atFunctionTop is true, don't leave one either.
     1027                ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
     1028                auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
    10261029                auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
    1027                 inFunction = false;
     1030                atFunctionTop = false;
    10281031                visitStatementList( node->kids );
    10291032        }
     
    10351038        MUTATE_START( node );
    10361039        {
    1037                 // do not enter a new scope if inFunction is true - needs to check old state before the assignment
    1038                 ValueGuard< bool > oldInFunction( inFunction );
    1039                 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
     1040                // Do not enter a new scope if atFunctionTop is true, don't leave one either.
     1041                ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
     1042                auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
    10401043                auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
    1041                 inFunction = false;
     1044                atFunctionTop = false;
    10421045                mutateStatementList( node->kids );
    10431046        }
     
    15171520
    15181521        maybeMutate_impl( node->block, *this );
     1522
     1523        MUTATE_END( Statement, node );
     1524}
     1525
     1526//--------------------------------------------------------------------------
     1527// SuspendStmt
     1528template< typename pass_type >
     1529void PassVisitor< pass_type >::visit( SuspendStmt * node ) {
     1530        VISIT_START( node );
     1531
     1532        maybeAccept_impl( node->then  , *this );
     1533
     1534        VISIT_END( node );
     1535}
     1536
     1537template< typename pass_type >
     1538void PassVisitor< pass_type >::visit( const SuspendStmt * node ) {
     1539        VISIT_START( node );
     1540
     1541        maybeAccept_impl( node->then  , *this );
     1542
     1543        VISIT_END( node );
     1544}
     1545
     1546template< typename pass_type >
     1547Statement * PassVisitor< pass_type >::mutate( SuspendStmt * node ) {
     1548        MUTATE_START( node );
     1549
     1550        maybeMutate_impl( node->then  , *this );
    15191551
    15201552        MUTATE_END( Statement, node );
  • src/Common/PassVisitor.proto.h

    r3c64c668 r58fe85a  
    3838        };
    3939
    40         std::stack< cleanup_t > cleanups;
     40        std::stack< cleanup_t, std::vector< cleanup_t > > cleanups;
    4141};
    4242
  • src/Common/ScopedMap.h

    r3c64c668 r58fe85a  
    9393
    9494                reference operator* () { return *it; }
    95                 pointer operator-> () { return it.operator->(); }
     95                pointer operator-> () const { return it.operator->(); }
    9696
    9797                iterator& operator++ () {
     
    249249
    250250        /// Gets the note at the given scope
     251        Note& getNote() { return scopes.back().note; }
     252        const Note& getNote() const { return scopes.back().note; }
    251253        Note& getNote( size_type i ) { return scopes[i].note; }
    252254        const Note& getNote( size_type i ) const { return scopes[i].note; }
  • src/Common/SemanticError.cc

    r3c64c668 r58fe85a  
    9090void SemanticErrorException::print() {
    9191        using std::to_string;
     92
     93        errors.sort([](const error & lhs, const error & rhs) -> bool {
     94                if(lhs.location.startsBefore(rhs.location)) return true;
     95                if(rhs.location.startsBefore(lhs.location)) return false;
     96
     97                return lhs.description < rhs.description;
     98        });
     99
    92100        for( auto err : errors ) {
    93101                std::cerr << ErrorHelpers::bold() << err.location << ErrorHelpers::error_str() << ErrorHelpers::reset_font() << err.description << std::endl;
  • src/Common/Stats/Heap.cc

    r3c64c668 r58fe85a  
    5353                const size_t passes_size = sizeof(passes) / sizeof(passes[0]);
    5454                size_t       passes_cnt = 1;
     55
     56                StatBlock    stacktrace_stats[100];
     57                size_t       stacktrace_stats_count = 0;
     58                bool         stacktrace_stats_enabled = true;
     59
     60                size_t       trace[1000];
     61                const size_t stacktrace_max_depth = sizeof(trace) / sizeof(size_t);
     62                size_t       stacktrace_depth;
     63
     64                size_t new_stacktrace_id(const char * const name) {
     65                        stacktrace_stats[stacktrace_stats_count].name = name;
     66                        return stacktrace_stats_count++;
     67                }
     68
     69                void stacktrace_push(size_t id) {
     70                        ++stacktrace_depth;
     71                        assertf(stacktrace_depth < stacktrace_max_depth, "Stack trace too deep: increase size of array in Heap.cc");
     72                        trace[stacktrace_depth] = id;
     73                }
     74
     75                void stacktrace_pop() {
     76                        assertf(stacktrace_depth > 0, "Invalid stack tracing operation: trace is empty");
     77                        --stacktrace_depth;
     78                }
    5579
    5680                void newPass( const char * const name ) {
     
    116140                        for(size_t i = 0; i < passes_cnt; i++) {
    117141                                print(passes[i], nc, total_mallocs, total_frees, overall_peak);
     142                        }
     143
     144                        print('-', nct);
     145                        std::cerr << std::setw(nc) << "Trace";
     146                        std::cerr << " |       Malloc Count |         Free Count |        Peak Allocs |" << std::endl;
     147
     148                        print('-', nct);
     149                        for (size_t i = 0; i < stacktrace_stats_count; i++) {
     150                                print(stacktrace_stats[i], nc, total_mallocs, total_frees, overall_peak);
    118151                        }
    119152                        print('-', nct);
     
    188221                                                = std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
    189222                                }
     223
     224                                if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
     225                                        stacktrace_stats[trace[stacktrace_depth]].mallocs++;
     226                                }
    190227                                return __malloc( size );
    191228                        }
     
    196233                                        passes[passes_cnt - 1].frees++;
    197234                                        passes[passes_cnt - 1].n_allocs--;
     235                                }
     236                                if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
     237                                        stacktrace_stats[trace[stacktrace_depth]].frees++;
    198238                                }
    199239                                return __free( ptr );
     
    208248                                                = std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
    209249                                }
     250                                if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
     251                                        stacktrace_stats[trace[stacktrace_depth]].mallocs++;
     252                                }
    210253                                return __calloc( nelem, size );
    211254                        }
     
    218261                                        passes[passes_cnt - 1].frees++;
    219262                                } // if
     263                                if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
     264                                        stacktrace_stats[trace[stacktrace_depth]].mallocs++;
     265                                        stacktrace_stats[trace[stacktrace_depth]].frees++;
     266                                }
    220267                                return s;
    221268                        }
  • src/Common/Stats/Heap.h

    r3c64c668 r58fe85a  
    2020                void newPass( const char * const name );
    2121                void print();
     22
     23                size_t new_stacktrace_id(const char * const name);
     24                void stacktrace_push(size_t id);
     25                void stacktrace_pop();
    2226        }
    2327}
  • src/Common/Stats/Stats.cc

    r3c64c668 r58fe85a  
    3535        }
    3636
     37        namespace ResolveTime {
     38                bool enabled = false;
     39        }
     40
    3741        struct {
    3842                const char * const opt;
     
    4347                { "heap"    , Heap::enabled },
    4448                { "time"    , Time::enabled },
     49                { "resolve" , ResolveTime::enabled },
    4550        };
    4651
  • src/Common/module.mk

    r3c64c668 r58fe85a  
    1717SRC_COMMON = \
    1818      Common/Assert.cc \
     19      Common/CodeLocation.h \
     20      Common/CodeLocationTools.hpp \
     21      Common/CodeLocationTools.cpp \
     22      Common/CompilerError.h \
     23      Common/Debug.h \
     24      Common/ErrorObjects.h \
    1925      Common/Eval.cc \
     26      Common/Examine.cc \
     27      Common/Examine.h \
     28      Common/FilterCombos.h \
     29      Common/Indenter.h \
    2030      Common/PassVisitor.cc \
     31      Common/PassVisitor.h \
     32      Common/PassVisitor.impl.h \
     33      Common/PassVisitor.proto.h \
     34      Common/PersistentMap.h \
     35      Common/ScopedMap.h \
    2136      Common/SemanticError.cc \
     37      Common/SemanticError.h \
     38      Common/Stats.h \
     39      Common/Stats/Base.h \
    2240      Common/Stats/Counter.cc \
     41      Common/Stats/Counter.h \
    2342      Common/Stats/Heap.cc \
     43      Common/Stats/Heap.h \
     44      Common/Stats/ResolveTime.cc \
     45      Common/Stats/ResolveTime.h \
    2446      Common/Stats/Stats.cc \
    2547      Common/Stats/Time.cc \
    26       Common/UniqueName.cc
     48      Common/Stats/Time.h \
     49      Common/UnimplementedError.h \
     50      Common/UniqueName.cc \
     51      Common/UniqueName.h \
     52      Common/utility.h \
     53      Common/VectorMap.h
    2754
    2855SRC += $(SRC_COMMON) Common/DebugMalloc.cc
  • src/Common/utility.h

    r3c64c668 r58fe85a  
    360360        reverse_iterate_t( T & ref ) : ref(ref) {}
    361361
    362         typedef typename T::reverse_iterator iterator;
    363         iterator begin() { return ref.rbegin(); }
    364         iterator end() { return ref.rend(); }
     362        // this does NOT work on const T!!!
     363        // typedef typename T::reverse_iterator iterator;
     364        auto begin() { return ref.rbegin(); }
     365        auto end() { return ref.rend(); }
    365366};
    366367
  • src/CompilationState.cc

    r3c64c668 r58fe85a  
    1414//
    1515
     16#include "config.h"
     17
    1618int
    1719        astp = false,
     
    2729        nopreludep = false,
    2830        genproto = false,
     31        deterministic_output = false,
     32        useNewAST = CFA_USE_NEW_AST,
    2933        nomainp = false,
    3034        parsep = false,
  • src/CompilationState.h

    r3c64c668 r58fe85a  
    2828        nopreludep,
    2929        genproto,
     30        deterministic_output,
     31        useNewAST,
    3032        nomainp,
    3133        parsep,
  • src/Concurrency/Keywords.cc

    r3c64c668 r58fe85a  
    1616#include "Concurrency/Keywords.h"
    1717
    18 #include <cassert>                 // for assert
    19 #include <string>                  // for string, operator==
    20 
    21 #include "Common/PassVisitor.h"    // for PassVisitor
    22 #include "Common/SemanticError.h"  // for SemanticError
    23 #include "Common/utility.h"        // for deleteAll, map_range
    24 #include "CodeGen/OperatorTable.h" // for isConstructor
    25 #include "InitTweak/InitTweak.h"   // for getPointerBase
    26 #include "SynTree/LinkageSpec.h"   // for Cforall
    27 #include "SynTree/Constant.h"      // for Constant
    28 #include "SynTree/Declaration.h"   // for StructDecl, FunctionDecl, ObjectDecl
    29 #include "SynTree/Expression.h"    // for VariableExpr, ConstantExpr, Untype...
    30 #include "SynTree/Initializer.h"   // for SingleInit, ListInit, Initializer ...
    31 #include "SynTree/Label.h"         // for Label
    32 #include "SynTree/Statement.h"     // for CompoundStmt, DeclStmt, ExprStmt
    33 #include "SynTree/Type.h"          // for StructInstType, Type, PointerType
    34 #include "SynTree/Visitor.h"       // for Visitor, acceptAll
     18#include <cassert>                        // for assert
     19#include <string>                         // for string, operator==
     20
     21#include <iostream>
     22
     23#include "Common/Examine.h"               // for isMainFor
     24#include "Common/PassVisitor.h"           // for PassVisitor
     25#include "Common/SemanticError.h"         // for SemanticError
     26#include "Common/utility.h"               // for deleteAll, map_range
     27#include "CodeGen/OperatorTable.h"        // for isConstructor
     28#include "ControlStruct/LabelGenerator.h" // for LebelGenerator
     29#include "InitTweak/InitTweak.h"          // for getPointerBase
     30#include "SynTree/LinkageSpec.h"          // for Cforall
     31#include "SynTree/Constant.h"             // for Constant
     32#include "SynTree/Declaration.h"          // for StructDecl, FunctionDecl, ObjectDecl
     33#include "SynTree/Expression.h"           // for VariableExpr, ConstantExpr, Untype...
     34#include "SynTree/Initializer.h"          // for SingleInit, ListInit, Initializer ...
     35#include "SynTree/Label.h"                // for Label
     36#include "SynTree/Statement.h"            // for CompoundStmt, DeclStmt, ExprStmt
     37#include "SynTree/Type.h"                 // for StructInstType, Type, PointerType
     38#include "SynTree/Visitor.h"              // for Visitor, acceptAll
     39#include "Virtual/Tables.h"
    3540
    3641class Attribute;
    3742
    3843namespace Concurrency {
     44        inline static std::string getVTableName( std::string const & exception_name ) {
     45                return exception_name.empty() ? std::string() : Virtual::vtableTypeName(exception_name);
     46        }
     47
     48        // Only detects threads constructed with the keyword thread.
     49        inline static bool isThread( DeclarationWithType * decl ) {
     50                Type * baseType = decl->get_type()->stripDeclarator();
     51                StructInstType * instType = dynamic_cast<StructInstType *>( baseType );
     52                if ( nullptr == instType ) { return false; }
     53                return instType->baseStruct->is_thread();
     54        }
     55
    3956        //=============================================================================================
    4057        // Pass declarations
     
    5370          public:
    5471
    55                 ConcurrentSueKeyword( std::string&& type_name, std::string&& field_name, std::string&& getter_name, std::string&& context_error, bool needs_main, AggregateDecl::Aggregate cast_target ) :
    56                   type_name( type_name ), field_name( field_name ), getter_name( getter_name ), context_error( context_error ), needs_main( needs_main ), cast_target( cast_target ) {}
     72                ConcurrentSueKeyword( std::string&& type_name, std::string&& field_name,
     73                        std::string&& getter_name, std::string&& context_error, std::string&& exception_name,
     74                        bool needs_main, AggregateDecl::Aggregate cast_target ) :
     75                  type_name( type_name ), field_name( field_name ), getter_name( getter_name ),
     76                  context_error( context_error ), exception_name( exception_name ),
     77                  vtable_name( getVTableName( exception_name ) ),
     78                  needs_main( needs_main ), cast_target( cast_target ) {}
    5779
    5880                virtual ~ConcurrentSueKeyword() {}
     
    6284
    6385                void handle( StructDecl * );
     86                void addVtableForward( StructDecl * );
    6487                FunctionDecl * forwardDeclare( StructDecl * );
    6588                ObjectDecl * addField( StructDecl * );
     
    7598                const std::string getter_name;
    7699                const std::string context_error;
     100                const std::string exception_name;
     101                const std::string vtable_name;
    77102                bool needs_main;
    78103                AggregateDecl::Aggregate cast_target;
     
    80105                StructDecl   * type_decl = nullptr;
    81106                FunctionDecl * dtor_decl = nullptr;
     107                StructDecl * except_decl = nullptr;
     108                StructDecl * vtable_decl = nullptr;
    82109        };
    83110
     
    100127                        "get_thread",
    101128                        "thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
     129                        "ThreadCancelled",
    102130                        true,
    103131                        AggregateDecl::Thread
     
    132160                        "get_coroutine",
    133161                        "coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n",
     162                        "CoroutineCancelled",
    134163                        true,
    135164                        AggregateDecl::Coroutine
     
    146175                }
    147176        };
     177
     178
    148179
    149180        //-----------------------------------------------------------------------------
     
    164195                        "get_monitor",
    165196                        "monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n",
     197                        "",
    166198                        false,
    167199                        AggregateDecl::Monitor
     
    177209                        mutateAll( translationUnit, impl );
    178210                }
     211        };
     212
     213        //-----------------------------------------------------------------------------
     214        //Handles generator type declarations :
     215        // generator MyGenerator {                   struct MyGenerator {
     216        //      int data;                                  int data;
     217        //      a_struct_t more_data;                      a_struct_t more_data;
     218        //                                =>             int __gen_next;
     219        // };                                        };
     220        //
     221        class GeneratorKeyword final : public ConcurrentSueKeyword {
     222          public:
     223
     224                GeneratorKeyword() : ConcurrentSueKeyword(
     225                        "$generator",
     226                        "__generator_state",
     227                        "get_generator",
     228                        "Unable to find builtin type $generator\n",
     229                        "",
     230                        true,
     231                        AggregateDecl::Generator
     232                )
     233                {}
     234
     235                virtual ~GeneratorKeyword() {}
     236
     237                virtual bool is_target( StructDecl * decl ) override final { return decl->is_generator(); }
     238
     239                static void implement( std::list< Declaration * > & translationUnit ) {
     240                        PassVisitor< GeneratorKeyword > impl;
     241                        mutateAll( translationUnit, impl );
     242                }
     243        };
     244
     245
     246        //-----------------------------------------------------------------------------
     247        class SuspendKeyword final : public WithStmtsToAdd, public WithGuards {
     248        public:
     249                SuspendKeyword() = default;
     250                virtual ~SuspendKeyword() = default;
     251
     252                void  premutate( FunctionDecl * );
     253                DeclarationWithType * postmutate( FunctionDecl * );
     254
     255                Statement * postmutate( SuspendStmt * );
     256
     257                static void implement( std::list< Declaration * > & translationUnit ) {
     258                        PassVisitor< SuspendKeyword > impl;
     259                        mutateAll( translationUnit, impl );
     260                }
     261
     262        private:
     263                bool is_real_suspend( FunctionDecl * );
     264
     265                Statement * make_generator_suspend( SuspendStmt * );
     266                Statement * make_coroutine_suspend( SuspendStmt * );
     267
     268                struct LabelPair {
     269                        Label obj;
     270                        int   idx;
     271                };
     272
     273                LabelPair make_label() {
     274                        labels.push_back( gen.newLabel("generator") );
     275                        return { labels.back(), int(labels.size()) };
     276                }
     277
     278                DeclarationWithType * in_generator = nullptr;
     279                FunctionDecl * decl_suspend = nullptr;
     280                std::vector<Label> labels;
     281                ControlStruct::LabelGenerator & gen = *ControlStruct::LabelGenerator::getGenerator();
    179282        };
    180283
     
    195298                std::list<DeclarationWithType*> findMutexArgs( FunctionDecl*, bool & first );
    196299                void validate( DeclarationWithType * );
    197                 void addDtorStatments( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
    198                 void addStatments( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
     300                void addDtorStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
     301                void addStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
     302                void addThreadDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args );
    199303
    200304                static void implement( std::list< Declaration * > & translationUnit ) {
     
    207311                StructDecl* guard_decl = nullptr;
    208312                StructDecl* dtor_guard_decl = nullptr;
     313                StructDecl* thread_guard_decl = nullptr;
    209314
    210315                static std::unique_ptr< Type > generic_func;
     
    251356                CoroutineKeyword        ::implement( translationUnit );
    252357                MonitorKeyword  ::implement( translationUnit );
     358                GeneratorKeyword  ::implement( translationUnit );
     359                SuspendKeyword    ::implement( translationUnit );
    253360        }
    254361
     
    283390                        handle( decl );
    284391                }
     392                else if ( !except_decl && exception_name == decl->name && decl->body ) {
     393                        except_decl = decl;
     394                }
     395                else if ( !vtable_decl && vtable_name == decl->name && decl->body ) {
     396                        vtable_decl = decl;
     397                }
     398                // Might be able to get ride of is target.
     399                assert( is_target(decl) == (cast_target == decl->kind) );
    285400                return decl;
    286401        }
    287402
    288403        DeclarationWithType * ConcurrentSueKeyword::postmutate( FunctionDecl * decl ) {
    289                 if( !type_decl ) return decl;
    290                 if( !CodeGen::isDestructor( decl->name ) ) return decl;
    291 
    292                 auto params = decl->type->parameters;
    293                 if( params.size() != 1 ) return decl;
    294 
    295                 auto type = dynamic_cast<ReferenceType*>( params.front()->get_type() );
    296                 if( !type ) return decl;
    297 
    298                 auto stype = dynamic_cast<StructInstType*>( type->base );
    299                 if( !stype ) return decl;
    300                 if( stype->baseStruct != type_decl ) return decl;
    301 
    302                 if( !dtor_decl ) dtor_decl = decl;
     404                if ( type_decl && isDestructorFor( decl, type_decl ) )
     405                        dtor_decl = decl;
     406                else if ( vtable_name.empty() )
     407                        ;
     408                else if( !decl->has_body() )
     409                        ;
     410                else if ( auto param = isMainFor( decl, cast_target ) ) {
     411                        // This should never trigger.
     412                        assert( vtable_decl );
     413                        // Should be safe because of isMainFor.
     414                        StructInstType * struct_type = static_cast<StructInstType *>(
     415                                static_cast<ReferenceType *>( param->get_type() )->base );
     416                        assert( struct_type );
     417
     418                        std::list< Expression * > poly_args = { new TypeExpr( struct_type->clone() ) };
     419                        ObjectDecl * vtable_object = Virtual::makeVtableInstance(
     420                                vtable_decl->makeInst( poly_args ), struct_type, nullptr );
     421                        declsToAddAfter.push_back( vtable_object );
     422                        declsToAddAfter.push_back( Virtual::makeGetExceptionFunction(
     423                                vtable_object, except_decl->makeInst( std::move( poly_args ) )
     424                        ) );
     425                }
     426
    303427                return decl;
    304428        }
     
    324448                if( !dtor_decl ) SemanticError( decl, context_error );
    325449
     450                addVtableForward( decl );
    326451                FunctionDecl * func = forwardDeclare( decl );
    327452                ObjectDecl * field = addField( decl );
    328453                addRoutines( field, func );
     454        }
     455
     456        void ConcurrentSueKeyword::addVtableForward( StructDecl * decl ) {
     457                if ( vtable_decl ) {
     458                        std::list< Expression * > poly_args = {
     459                                new TypeExpr( new StructInstType( noQualifiers, decl ) ),
     460                        };
     461                        declsToAddBefore.push_back( Virtual::makeGetExceptionForward(
     462                                vtable_decl->makeInst( poly_args ),
     463                                except_decl->makeInst( poly_args )
     464                        ) );
     465                        declsToAddBefore.push_back( Virtual::makeVtableForward(
     466                                vtable_decl->makeInst( move( poly_args ) ) ) );
     467                // Its only an error if we want a vtable and don't have one.
     468                } else if ( ! vtable_name.empty() ) {
     469                        SemanticError( decl, context_error );
     470                }
    329471        }
    330472
     
    434576                                                new CastExpr(
    435577                                                        new VariableExpr( func->get_functionType()->get_parameters().front() ),
    436                                                         func->get_functionType()->get_parameters().front()->get_type()->stripReferences()->clone()
     578                                                        func->get_functionType()->get_parameters().front()->get_type()->stripReferences()->clone(),
     579                                                        false
    437580                                                )
    438581                                        )
     
    446589
    447590                declsToAddAfter.push_back( get_decl );
    448 
    449                 // get_decl->fixUniqueId();
    450         }
     591        }
     592
     593        //=============================================================================================
     594        // Suspend keyword implementation
     595        //=============================================================================================
     596        bool SuspendKeyword::is_real_suspend( FunctionDecl * func ) {
     597                if(isMangled(func->linkage)) return false; // the real suspend isn't mangled
     598                if(func->name != "__cfactx_suspend") return false; // the real suspend has a specific name
     599                if(func->type->parameters.size() != 0) return false; // Too many parameters
     600                if(func->type->returnVals.size() != 0) return false; // Too many return values
     601
     602                return true;
     603        }
     604
     605        void SuspendKeyword::premutate( FunctionDecl * func ) {
     606                GuardValue(in_generator);
     607                in_generator = nullptr;
     608
     609                // Is this the real suspend?
     610                if(is_real_suspend(func)) {
     611                        decl_suspend = decl_suspend ? decl_suspend : func;
     612                        return;
     613                }
     614
     615                // Is this the main of a generator?
     616                auto param = isMainFor( func, AggregateDecl::Aggregate::Generator );
     617                if(!param) return;
     618
     619                if(func->type->returnVals.size() != 0) SemanticError(func->location, "Generator main must return void");
     620
     621                in_generator = param;
     622                GuardValue(labels);
     623                labels.clear();
     624        }
     625
     626        DeclarationWithType * SuspendKeyword::postmutate( FunctionDecl * func ) {
     627                if( !func->statements ) return func; // Not the actual definition, don't do anything
     628                if( !in_generator     ) return func; // Not in a generator, don't do anything
     629                if( labels.empty()    ) return func; // Generator has no states, nothing to do, could throw a warning
     630
     631                // This is a generator main, we need to add the following code to the top
     632                // static void * __generator_labels[] = {&&s0, &&s1, ...};
     633                // goto * __generator_labels[gen.__generator_state];
     634                const auto & loc = func->location;
     635
     636                const auto first_label = gen.newLabel("generator");
     637
     638                // for each label add to declaration
     639                std::list<Initializer*> inits = { new SingleInit( new LabelAddressExpr( first_label ) ) };
     640                for(const auto & label : labels) {
     641                        inits.push_back(
     642                                new SingleInit(
     643                                        new LabelAddressExpr( label )
     644                                )
     645                        );
     646                }
     647                auto init = new ListInit(std::move(inits), noDesignators, true);
     648                labels.clear();
     649
     650                // create decl
     651                auto decl = new ObjectDecl(
     652                        "__generator_labels",
     653                        Type::StorageClasses( Type::Static ),
     654                        LinkageSpec::AutoGen,
     655                        nullptr,
     656                        new ArrayType(
     657                                Type::Qualifiers(),
     658                                new PointerType(
     659                                        Type::Qualifiers(),
     660                                        new VoidType( Type::Qualifiers() )
     661                                ),
     662                                nullptr,
     663                                false, false
     664                        ),
     665                        init
     666                );
     667
     668                // create the goto
     669                assert(in_generator);
     670
     671                auto go_decl = new ObjectDecl(
     672                        "__generator_label",
     673                        noStorageClasses,
     674                        LinkageSpec::AutoGen,
     675                        nullptr,
     676                        new PointerType(
     677                                Type::Qualifiers(),
     678                                new VoidType( Type::Qualifiers() )
     679                        ),
     680                        new SingleInit(
     681                                new UntypedExpr(
     682                                        new NameExpr("?[?]"),
     683                                        {
     684                                                new NameExpr("__generator_labels"),
     685                                                new UntypedMemberExpr(
     686                                                        new NameExpr("__generator_state"),
     687                                                        new VariableExpr( in_generator )
     688                                                )
     689                                        }
     690                                )
     691                        )
     692                );
     693                go_decl->location = loc;
     694
     695                auto go = new BranchStmt(
     696                        new VariableExpr( go_decl ),
     697                        BranchStmt::Goto
     698                );
     699                go->location = loc;
     700                go->computedTarget->location = loc;
     701
     702                auto noop = new NullStmt({ first_label });
     703                noop->location = loc;
     704
     705                // wrap everything in a nice compound
     706                auto body = new CompoundStmt({
     707                        new DeclStmt( decl ),
     708                        new DeclStmt( go_decl ),
     709                        go,
     710                        noop,
     711                        func->statements
     712                });
     713                body->location   = loc;
     714                func->statements = body;
     715
     716                return func;
     717        }
     718
     719        Statement * SuspendKeyword::postmutate( SuspendStmt * stmt ) {
     720                SuspendStmt::Type type = stmt->type;
     721                if(type == SuspendStmt::None) {
     722                        // This suspend has a implicit target, find it
     723                        type = in_generator ? SuspendStmt::Generator : SuspendStmt::Coroutine;
     724                }
     725
     726                // Check that the target makes sense
     727                if(!in_generator && type == SuspendStmt::Generator) SemanticError( stmt->location, "'suspend generator' must be used inside main of generator type.");
     728
     729                // Act appropriately
     730                switch(type) {
     731                        case SuspendStmt::Generator: return make_generator_suspend(stmt);
     732                        case SuspendStmt::Coroutine: return make_coroutine_suspend(stmt);
     733                        default: abort();
     734                }
     735        }
     736
     737        Statement * SuspendKeyword::make_generator_suspend( SuspendStmt * stmt ) {
     738                assert(in_generator);
     739                // Target code is :
     740                //   gen.__generator_state = X;
     741                //   { THEN }
     742                //   return;
     743                //   __gen_X:;
     744
     745                // Save the location and delete the old statement, we only need the location from this point on
     746                auto loc = stmt->location;
     747
     748                // Build the label and get its index
     749                auto label = make_label();
     750
     751                // Create the context saving statement
     752                auto save = new ExprStmt( new UntypedExpr(
     753                        new NameExpr( "?=?" ),
     754                        {
     755                                new UntypedMemberExpr(
     756                                        new NameExpr("__generator_state"),
     757                                        new VariableExpr( in_generator )
     758                                ),
     759                                new ConstantExpr(
     760                                        Constant::from_int( label.idx )
     761                                )
     762                        }
     763                ));
     764                assert(save->expr);
     765                save->location = loc;
     766                stmtsToAddBefore.push_back( save );
     767
     768                // if we have a then add it here
     769                auto then = stmt->then;
     770                stmt->then = nullptr;
     771                delete stmt;
     772                if(then) stmtsToAddBefore.push_back( then );
     773
     774                // Create the return statement
     775                auto ret = new ReturnStmt( nullptr );
     776                ret->location = loc;
     777                stmtsToAddBefore.push_back( ret );
     778
     779                // Create the null statement with the created label
     780                auto noop = new NullStmt({ label.obj });
     781                noop->location = loc;
     782
     783                // Return the null statement to take the place of the previous statement
     784                return noop;
     785        }
     786
     787        Statement * SuspendKeyword::make_coroutine_suspend( SuspendStmt * stmt ) {
     788                if(stmt->then) SemanticError( stmt->location, "Compound statement following coroutines is not implemented.");
     789
     790                // Save the location and delete the old statement, we only need the location from this point on
     791                auto loc = stmt->location;
     792                delete stmt;
     793
     794                // Create the call expression
     795                if(!decl_suspend) SemanticError( loc, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>\n");
     796                auto expr = new UntypedExpr( VariableExpr::functionPointer( decl_suspend ) );
     797                expr->location = loc;
     798
     799                // Change this statement into a regular expr
     800                assert(expr);
     801                auto nstmt = new ExprStmt( expr );
     802                nstmt->location = loc;
     803                return nstmt;
     804        }
     805
    451806
    452807        //=============================================================================================
     
    458813                bool first = false;
    459814                std::list<DeclarationWithType*> mutexArgs = findMutexArgs( decl, first );
    460                 bool isDtor = CodeGen::isDestructor( decl->name );
     815                bool const isDtor = CodeGen::isDestructor( decl->name );
    461816
    462817                // Is this function relevant to monitors
     
    506861
    507862                // Instrument the body
    508                 if( isDtor ) {
    509                         addDtorStatments( decl, body, mutexArgs );
     863                if ( isDtor && isThread( mutexArgs.front() ) ) {
     864                        if( !thread_guard_decl ) {
     865                                SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>\n" );
     866                        }
     867                        addThreadDtorStatements( decl, body, mutexArgs );
     868                }
     869                else if ( isDtor ) {
     870                        addDtorStatements( decl, body, mutexArgs );
    510871                }
    511872                else {
    512                         addStatments( decl, body, mutexArgs );
     873                        addStatements( decl, body, mutexArgs );
    513874                }
    514875        }
     
    527888                        assert( !dtor_guard_decl );
    528889                        dtor_guard_decl = decl;
     890                }
     891                else if( decl->name == "thread_dtor_guard_t" && decl->body ) {
     892                        assert( !thread_guard_decl );
     893                        thread_guard_decl = decl;
    529894                }
    530895        }
     
    565930        }
    566931
    567         void MutexKeyword::addDtorStatments( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
     932        void MutexKeyword::addDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
    568933                Type * arg_type = args.front()->get_type()->clone();
    569934                arg_type->set_mutex( false );
     
    583948                        new SingleInit( new UntypedExpr(
    584949                                new NameExpr( "get_monitor" ),
    585                                 {  new CastExpr( new VariableExpr( args.front() ), arg_type ) }
     950                                {  new CastExpr( new VariableExpr( args.front() ), arg_type, false ) }
    586951                        ))
    587952                );
     
    604969                                        {
    605970                                                new SingleInit( new AddressExpr( new VariableExpr( monitors ) ) ),
    606                                                 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone() ) )
     971                                                new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone(), false ) ),
     972                                                new SingleInit( new ConstantExpr( Constant::from_bool( false ) ) )
    607973                                        },
    608974                                        noDesignators,
     
    613979
    614980                //$monitor * __monitors[] = { get_monitor(a), get_monitor(b) };
    615                 body->push_front( new DeclStmt( monitors) );
    616         }
    617 
    618         void MutexKeyword::addStatments( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
     981                body->push_front( new DeclStmt( monitors ) );
     982        }
     983
     984        void MutexKeyword::addThreadDtorStatements(
     985                        FunctionDecl*, CompoundStmt * body,
     986                        const std::list<DeclarationWithType * > & args ) {
     987                assert( args.size() == 1 );
     988                DeclarationWithType * arg = args.front();
     989                Type * arg_type = arg->get_type()->clone();
     990                assert( arg_type->get_mutex() );
     991                arg_type->set_mutex( false );
     992
     993                // thread_dtor_guard_t __guard = { this, intptr( 0 ) };
     994                body->push_front(
     995                        new DeclStmt( new ObjectDecl(
     996                                "__guard",
     997                                noStorageClasses,
     998                                LinkageSpec::Cforall,
     999                                nullptr,
     1000                                new StructInstType(
     1001                                        noQualifiers,
     1002                                        thread_guard_decl
     1003                                ),
     1004                                new ListInit(
     1005                                        {
     1006                                                new SingleInit( new CastExpr( new VariableExpr( arg ), arg_type ) ),
     1007                                                new SingleInit( new UntypedExpr(
     1008                                                        new NameExpr( "intptr" ), {
     1009                                                                new ConstantExpr( Constant::from_int( 0 ) ),
     1010                                                        }
     1011                                                ) ),
     1012                                        },
     1013                                        noDesignators,
     1014                                        true
     1015                                )
     1016                        ))
     1017                );
     1018        }
     1019
     1020        void MutexKeyword::addStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
    6191021                ObjectDecl * monitors = new ObjectDecl(
    6201022                        "__monitors",
     
    6411043                                        return new SingleInit( new UntypedExpr(
    6421044                                                new NameExpr( "get_monitor" ),
    643                                                 {  new CastExpr( new VariableExpr( var ), type ) }
     1045                                                {  new CastExpr( new VariableExpr( var ), type, false ) }
    6441046                                        ) );
    6451047                                })
     
    6651067                                                new SingleInit( new VariableExpr( monitors ) ),
    6661068                                                new SingleInit( new ConstantExpr( Constant::from_ulong( args.size() ) ) ),
    667                                                 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone() ) )
     1069                                                new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone(), false ) )
    6681070                                        },
    6691071                                        noDesignators,
     
    7271129// tab-width: 4 //
    7281130// End: //
     1131
  • src/Concurrency/Waitfor.cc

    r3c64c668 r58fe85a  
    384384                                                                decl_monitor
    385385                                                        )
    386                                                 )
     386                                                ),
     387                                                false
    387388                                        );
    388389
     
    408409                        new CompoundStmt({
    409410                                makeAccStatement( acceptables, index, "is_dtor", detectIsDtor( clause.target.function )                                    , indexer ),
    410                                 makeAccStatement( acceptables, index, "func"   , new CastExpr( clause.target.function, fptr_t )                            , indexer ),
     411                                makeAccStatement( acceptables, index, "func"   , new CastExpr( clause.target.function, fptr_t, false )                     , indexer ),
    411412                                makeAccStatement( acceptables, index, "data"   , new VariableExpr( monitors )                                              , indexer ),
    412413                                makeAccStatement( acceptables, index, "size"   , new ConstantExpr( Constant::from_ulong( clause.target.arguments.size() ) ), indexer ),
     
    531532                                                                decl_mask
    532533                                                        )
    533                                                 )
     534                                                ),
     535                                                false
    534536                                        ),
    535537                                        timeout
  • src/Concurrency/module.mk

    r3c64c668 r58fe85a  
    1515###############################################################################
    1616
    17 SRC += Concurrency/Keywords.cc Concurrency/Waitfor.cc
     17SRC += Concurrency/Keywords.cc Concurrency/Keywords.h Concurrency/Waitfor.cc Concurrency/Waitfor.h
    1818SRCDEMANGLE += Concurrency/Keywords.cc
    1919
  • src/ControlStruct/ExceptTranslate.cc

    r3c64c668 r58fe85a  
    99// Author           : Andrew Beach
    1010// Created On       : Wed Jun 14 16:49:00 2017
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Dec 13 23:40:15 2019
    13 // Update Count     : 12
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Wed Jun 24 11:18:00 2020
     13// Update Count     : 17
    1414//
    1515
     
    6464        }
    6565
    66         class ExceptionMutatorCore : public WithGuards {
    67                 enum Context { NoHandler, TerHandler, ResHandler };
    68 
    69                 // Also need to handle goto, break & continue.
    70                 // They need to be cut off in a ResHandler, until we enter another
    71                 // loop, switch or the goto stays within the function.
    72 
    73                 Context cur_context;
    74 
    75                 // The current (innermost) termination handler exception declaration.
    76                 ObjectDecl * handler_except_decl;
    77 
     66        class ThrowMutatorCore : public WithGuards {
     67                ObjectDecl * terminate_handler_except;
     68                enum Context { NoHandler, TerHandler, ResHandler } cur_context;
     69
     70                // The helper functions for code/syntree generation.
     71                Statement * create_either_throw(
     72                        ThrowStmt * throwStmt, const char * throwFunc );
     73                Statement * create_terminate_rethrow( ThrowStmt * throwStmt );
     74
     75        public:
     76                ThrowMutatorCore() :
     77                        terminate_handler_except( nullptr ),
     78                        cur_context( NoHandler )
     79                {}
     80
     81                void premutate( CatchStmt *catchStmt );
     82                Statement * postmutate( ThrowStmt *throwStmt );
     83        };
     84
     85        // ThrowStmt Mutation Helpers
     86
     87        Statement * ThrowMutatorCore::create_either_throw(
     88                        ThrowStmt * throwStmt, const char * throwFunc ) {
     89                // `throwFunc`( `throwStmt->get_name()` );
     90                UntypedExpr * call = new UntypedExpr( new NameExpr( throwFunc ) );
     91                call->get_args().push_back( throwStmt->get_expr() );
     92                throwStmt->set_expr( nullptr );
     93                delete throwStmt;
     94                return new ExprStmt( call );
     95        }
     96
     97        Statement * ThrowMutatorCore::create_terminate_rethrow(
     98                        ThrowStmt *throwStmt ) {
     99                // { `terminate_handler_except` = 0p; __rethrow_terminate(); }
     100                assert( nullptr == throwStmt->get_expr() );
     101                assert( terminate_handler_except );
     102
     103                CompoundStmt * result = new CompoundStmt();
     104                result->labels =  throwStmt->labels;
     105                result->push_back( new ExprStmt( UntypedExpr::createAssign(
     106                        nameOf( terminate_handler_except ),
     107                        new ConstantExpr( Constant::null(
     108                                terminate_handler_except->get_type()->clone()
     109                                ) )
     110                        ) ) );
     111                result->push_back( new ExprStmt(
     112                        new UntypedExpr( new NameExpr( "__cfaehm_rethrow_terminate" ) )
     113                        ) );
     114                delete throwStmt;
     115                return result;
     116        }
     117
     118        // Visiting/Mutating Functions
     119
     120        void ThrowMutatorCore::premutate( CatchStmt *catchStmt ) {
     121                // Validate the statement's form.
     122                ObjectDecl * decl = dynamic_cast<ObjectDecl *>( catchStmt->get_decl() );
     123                // Also checking the type would be nice.
     124                if ( !decl || !dynamic_cast<PointerType *>( decl->type ) ) {
     125                        std::string kind = (CatchStmt::Terminate == catchStmt->kind) ? "catch" : "catchResume";
     126                        SemanticError( catchStmt->location, kind + " must have pointer to an exception type" );
     127                }
     128
     129                // Track the handler context.
     130                GuardValue( cur_context );
     131                if ( CatchStmt::Terminate == catchStmt->get_kind() ) {
     132                        cur_context = TerHandler;
     133
     134                        GuardValue( terminate_handler_except );
     135                        terminate_handler_except = decl;
     136                } else {
     137                        cur_context = ResHandler;
     138                }
     139        }
     140
     141        Statement * ThrowMutatorCore::postmutate( ThrowStmt *throwStmt ) {
     142                // Ignoring throwStmt->get_target() for now.
     143                if ( ThrowStmt::Terminate == throwStmt->get_kind() ) {
     144                        if ( throwStmt->get_expr() ) {
     145                                return create_either_throw( throwStmt, "$throw" );
     146                        } else if ( TerHandler == cur_context ) {
     147                                return create_terminate_rethrow( throwStmt );
     148                        } else {
     149                                abort("Invalid throw in %s at %i\n",
     150                                        throwStmt->location.filename.c_str(),
     151                                        throwStmt->location.first_line);
     152                        }
     153                } else {
     154                        if ( throwStmt->get_expr() ) {
     155                                return create_either_throw( throwStmt, "$throwResume" );
     156                        } else if ( ResHandler == cur_context ) {
     157                                // This has to be handled later.
     158                                return throwStmt;
     159                        } else {
     160                                abort("Invalid throwResume in %s at %i\n",
     161                                        throwStmt->location.filename.c_str(),
     162                                        throwStmt->location.first_line);
     163                        }
     164                }
     165        }
     166
     167        class TryMutatorCore {
    78168                // The built in types used in translation.
    79169                StructDecl * except_decl;
     
    82172
    83173                // The many helper functions for code/syntree generation.
    84                 Statement * create_given_throw(
    85                         const char * throwFunc, ThrowStmt * throwStmt );
    86                 Statement * create_terminate_throw( ThrowStmt * throwStmt );
    87                 Statement * create_terminate_rethrow( ThrowStmt * throwStmt );
    88                 Statement * create_resume_throw( ThrowStmt * throwStmt );
    89                 Statement * create_resume_rethrow( ThrowStmt * throwStmt );
    90174                CompoundStmt * take_try_block( TryStmt * tryStmt );
    91175                FunctionDecl * create_try_wrapper( CompoundStmt * body );
     
    101185                FunctionDecl * create_finally_wrapper( TryStmt * tryStmt );
    102186                ObjectDecl * create_finally_hook( FunctionDecl * finally_wrapper );
     187                Statement * create_resume_rethrow( ThrowStmt * throwStmt );
    103188
    104189                // Types used in translation, make sure to use clone.
     
    121206
    122207        public:
    123                 ExceptionMutatorCore() :
    124                         cur_context( NoHandler ),
    125                         handler_except_decl( nullptr ),
     208                TryMutatorCore() :
    126209                        except_decl( nullptr ), node_decl( nullptr ), hook_decl( nullptr ),
    127210                        try_func_t( noQualifiers, false ),
     
    132215                {}
    133216
    134                 void premutate( CatchStmt *catchStmt );
    135217                void premutate( StructDecl *structDecl );
     218                Statement * postmutate( TryStmt *tryStmt );
    136219                Statement * postmutate( ThrowStmt *throwStmt );
    137                 Statement * postmutate( TryStmt *tryStmt );
    138220        };
    139221
    140         void ExceptionMutatorCore::init_func_types() {
     222        void TryMutatorCore::init_func_types() {
    141223                assert( except_decl );
    142224
     
    196278        }
    197279
    198         // ThrowStmt Mutation Helpers
    199 
    200         Statement * ExceptionMutatorCore::create_given_throw(
    201                         const char * throwFunc, ThrowStmt * throwStmt ) {
    202                 // `throwFunc`( `throwStmt->get_name` );
    203                 UntypedExpr * call = new UntypedExpr( new NameExpr( throwFunc ) );
    204                 call->get_args().push_back( throwStmt->get_expr() );
    205                 throwStmt->set_expr( nullptr );
    206                 delete throwStmt;
    207                 return new ExprStmt( call );
    208         }
    209 
    210         Statement * ExceptionMutatorCore::create_terminate_throw(
    211                         ThrowStmt *throwStmt ) {
    212                 // __throw_terminate( `throwStmt->get_name()` ); }
    213                 return create_given_throw( "__cfaabi_ehm__throw_terminate", throwStmt );
    214         }
    215 
    216         Statement * ExceptionMutatorCore::create_terminate_rethrow(
    217                         ThrowStmt *throwStmt ) {
    218                 // { `handler_except_decl` = NULL; __rethrow_terminate(); }
    219                 assert( nullptr == throwStmt->get_expr() );
    220                 assert( handler_except_decl );
    221 
    222                 CompoundStmt * result = new CompoundStmt();
    223                 result->labels =  throwStmt->labels;
    224                 result->push_back( new ExprStmt( UntypedExpr::createAssign(
    225                         nameOf( handler_except_decl ),
    226                         new ConstantExpr( Constant::null(
    227                                 new PointerType(
    228                                         noQualifiers,
    229                                         handler_except_decl->get_type()->clone()
    230                                         )
    231                                 ) )
    232                         ) ) );
    233                 result->push_back( new ExprStmt(
    234                         new UntypedExpr( new NameExpr( "__cfaabi_ehm__rethrow_terminate" ) )
    235                         ) );
    236                 delete throwStmt;
    237                 return result;
    238         }
    239 
    240         Statement * ExceptionMutatorCore::create_resume_throw(
    241                         ThrowStmt *throwStmt ) {
    242                 // __throw_resume( `throwStmt->get_name` );
    243                 return create_given_throw( "__cfaabi_ehm__throw_resume", throwStmt );
    244         }
    245 
    246         Statement * ExceptionMutatorCore::create_resume_rethrow(
    247                         ThrowStmt *throwStmt ) {
    248                 // return false;
    249                 Statement * result = new ReturnStmt(
    250                         new ConstantExpr( Constant::from_bool( false ) )
    251                         );
    252                 result->labels = throwStmt->labels;
    253                 delete throwStmt;
    254                 return result;
    255         }
    256 
    257280        // TryStmt Mutation Helpers
    258281
    259         CompoundStmt * ExceptionMutatorCore::take_try_block( TryStmt *tryStmt ) {
     282        CompoundStmt * TryMutatorCore::take_try_block( TryStmt *tryStmt ) {
    260283                CompoundStmt * block = tryStmt->get_block();
    261284                tryStmt->set_block( nullptr );
     
    263286        }
    264287
    265         FunctionDecl * ExceptionMutatorCore::create_try_wrapper(
     288        FunctionDecl * TryMutatorCore::create_try_wrapper(
    266289                        CompoundStmt *body ) {
    267290
     
    270293        }
    271294
    272         FunctionDecl * ExceptionMutatorCore::create_terminate_catch(
     295        FunctionDecl * TryMutatorCore::create_terminate_catch(
    273296                        CatchList &handlers ) {
    274297                std::list<CaseStmt *> handler_wrappers;
     
    309332                        local_except->get_attributes().push_back( new Attribute(
    310333                                "cleanup",
    311                                 { new NameExpr( "__cfaabi_ehm__cleanup_terminate" ) }
     334                                { new NameExpr( "__cfaehm_cleanup_terminate" ) }
    312335                                ) );
    313336
     
    350373        // Create a single check from a moddified handler.
    351374        // except_obj is referenced, modded_handler will be freed.
    352         CompoundStmt * ExceptionMutatorCore::create_single_matcher(
     375        CompoundStmt * TryMutatorCore::create_single_matcher(
    353376                        DeclarationWithType * except_obj, CatchStmt * modded_handler ) {
    354377                // {
     
    388411        }
    389412
    390         FunctionDecl * ExceptionMutatorCore::create_terminate_match(
     413        FunctionDecl * TryMutatorCore::create_terminate_match(
    391414                        CatchList &handlers ) {
    392415                // int match(exception * except) {
     
    425448        }
    426449
    427         CompoundStmt * ExceptionMutatorCore::create_terminate_caller(
     450        CompoundStmt * TryMutatorCore::create_terminate_caller(
    428451                        FunctionDecl * try_wrapper,
    429452                        FunctionDecl * terminate_catch,
    430453                        FunctionDecl * terminate_match ) {
    431                 // { __cfaabi_ehm__try_terminate(`try`, `catch`, `match`); }
     454                // { __cfaehm_try_terminate(`try`, `catch`, `match`); }
    432455
    433456                UntypedExpr * caller = new UntypedExpr( new NameExpr(
    434                         "__cfaabi_ehm__try_terminate" ) );
     457                        "__cfaehm_try_terminate" ) );
    435458                std::list<Expression *>& args = caller->get_args();
    436459                args.push_back( nameOf( try_wrapper ) );
     
    443466        }
    444467
    445         FunctionDecl * ExceptionMutatorCore::create_resume_handler(
     468        FunctionDecl * TryMutatorCore::create_resume_handler(
    446469                        CatchList &handlers ) {
    447470                // bool handle(exception * except) {
     
    480503        }
    481504
    482         CompoundStmt * ExceptionMutatorCore::create_resume_wrapper(
     505        CompoundStmt * TryMutatorCore::create_resume_wrapper(
    483506                        Statement * wraps,
    484507                        FunctionDecl * resume_handler ) {
     
    486509
    487510                // struct __try_resume_node __resume_node
    488                 //      __attribute__((cleanup( __cfaabi_ehm__try_resume_cleanup )));
     511                //      __attribute__((cleanup( __cfaehm_try_resume_cleanup )));
    489512                // ** unwinding of the stack here could cause problems **
    490513                // ** however I don't think that can happen currently **
    491                 // __cfaabi_ehm__try_resume_setup( &__resume_node, resume_handler );
     514                // __cfaehm_try_resume_setup( &__resume_node, resume_handler );
    492515
    493516                std::list< Attribute * > attributes;
     
    495518                        std::list< Expression * > attr_params;
    496519                        attr_params.push_back( new NameExpr(
    497                                 "__cfaabi_ehm__try_resume_cleanup" ) );
     520                                "__cfaehm_try_resume_cleanup" ) );
    498521                        attributes.push_back( new Attribute( "cleanup", attr_params ) );
    499522                }
     
    514537
    515538                UntypedExpr *setup = new UntypedExpr( new NameExpr(
    516                         "__cfaabi_ehm__try_resume_setup" ) );
     539                        "__cfaehm_try_resume_setup" ) );
    517540                setup->get_args().push_back( new AddressExpr( nameOf( obj ) ) );
    518541                setup->get_args().push_back( nameOf( resume_handler ) );
     
    524547        }
    525548
    526         FunctionDecl * ExceptionMutatorCore::create_finally_wrapper(
     549        FunctionDecl * TryMutatorCore::create_finally_wrapper(
    527550                        TryStmt * tryStmt ) {
    528                 // void finally() { <finally code> }
     551                // void finally() { `finally->block` }
    529552                FinallyStmt * finally = tryStmt->get_finally();
    530553                CompoundStmt * body = finally->get_block();
     
    537560        }
    538561
    539         ObjectDecl * ExceptionMutatorCore::create_finally_hook(
     562        ObjectDecl * TryMutatorCore::create_finally_hook(
    540563                        FunctionDecl * finally_wrapper ) {
    541                 // struct __cfaabi_ehm__cleanup_hook __finally_hook
    542                 //      __attribute__((cleanup( finally_wrapper )));
     564                // struct __cfaehm_cleanup_hook __finally_hook
     565                //      __attribute__((cleanup( `finally_wrapper` )));
    543566
    544567                // Make Cleanup Attribute.
     
    564587        }
    565588
     589        Statement * TryMutatorCore::create_resume_rethrow( ThrowStmt *throwStmt ) {
     590                // return false;
     591                Statement * result = new ReturnStmt(
     592                        new ConstantExpr( Constant::from_bool( false ) )
     593                        );
     594                result->labels = throwStmt->labels;
     595                delete throwStmt;
     596                return result;
     597        }
     598
    566599        // Visiting/Mutating Functions
    567         void ExceptionMutatorCore::premutate( CatchStmt *catchStmt ) {
    568                 // Validate the Statement's form.
    569                 ObjectDecl * decl =
    570                         dynamic_cast<ObjectDecl *>( catchStmt->get_decl() );
    571                 if ( decl && true /* check decl->get_type() */ ) {
    572                         // Pass.
    573                 } else if ( CatchStmt::Terminate == catchStmt->get_kind() ) {
    574                         SemanticError(catchStmt->location, "catch must have exception type");
    575                 } else {
    576                         SemanticError(catchStmt->location, "catchResume must have exception type");
    577                 }
    578 
    579                 // Track the handler context.
    580                 GuardValue( cur_context );
    581                 if ( CatchStmt::Terminate == catchStmt->get_kind() ) {
    582                         cur_context = TerHandler;
    583 
    584                         GuardValue( handler_except_decl );
    585                         handler_except_decl = decl;
    586                 } else {
    587                         cur_context = ResHandler;
    588                 }
    589         }
    590 
    591         void ExceptionMutatorCore::premutate( StructDecl *structDecl ) {
     600        void TryMutatorCore::premutate( StructDecl *structDecl ) {
    592601                if ( !structDecl->has_body() ) {
    593602                        // Skip children?
    594603                        return;
    595                 } else if ( structDecl->get_name() == "__cfaabi_ehm__base_exception_t" ) {
     604                } else if ( structDecl->get_name() == "__cfaehm_base_exception_t" ) {
    596605                        assert( nullptr == except_decl );
    597606                        except_decl = structDecl;
    598607                        init_func_types();
    599                 } else if ( structDecl->get_name() == "__cfaabi_ehm__try_resume_node" ) {
     608                } else if ( structDecl->get_name() == "__cfaehm_try_resume_node" ) {
    600609                        assert( nullptr == node_decl );
    601610                        node_decl = structDecl;
    602                 } else if ( structDecl->get_name() == "__cfaabi_ehm__cleanup_hook" ) {
     611                } else if ( structDecl->get_name() == "__cfaehm_cleanup_hook" ) {
    603612                        assert( nullptr == hook_decl );
    604613                        hook_decl = structDecl;
    605614                }
    606                 // Later we might get the exception type as well.
    607         }
    608 
    609         Statement * ExceptionMutatorCore::postmutate( ThrowStmt *throwStmt ) {
    610                 assert( except_decl );
    611 
    612                 // Ignoring throwStmt->get_target() for now.
    613                 if ( ThrowStmt::Terminate == throwStmt->get_kind() ) {
    614                         if ( throwStmt->get_expr() ) {
    615                                 return create_terminate_throw( throwStmt );
    616                         } else if ( TerHandler == cur_context ) {
    617                                 return create_terminate_rethrow( throwStmt );
    618                         } else {
    619                                 abort("Invalid throw in %s at %i\n",
    620                                         throwStmt->location.filename.c_str(),
    621                                         throwStmt->location.first_line);
    622                         }
    623                 } else {
    624                         if ( throwStmt->get_expr() ) {
    625                                 return create_resume_throw( throwStmt );
    626                         } else if ( ResHandler == cur_context ) {
    627                                 return create_resume_rethrow( throwStmt );
    628                         } else {
    629                                 abort("Invalid throwResume in %s at %i\n",
    630                                         throwStmt->location.filename.c_str(),
    631                                         throwStmt->location.first_line);
    632                         }
    633                 }
    634         }
    635 
    636         Statement * ExceptionMutatorCore::postmutate( TryStmt *tryStmt ) {
     615        }
     616
     617        Statement * TryMutatorCore::postmutate( TryStmt *tryStmt ) {
    637618                assert( except_decl );
    638619                assert( node_decl );
     
    688669        }
    689670
    690         void translateEHM( std::list< Declaration *> & translationUnit ) {
    691                 PassVisitor<ExceptionMutatorCore> translator;
     671        Statement * TryMutatorCore::postmutate( ThrowStmt *throwStmt ) {
     672                // Only valid `throwResume;` statements should remain. (2/3 checks)
     673                assert( ThrowStmt::Resume == throwStmt->kind && ! throwStmt->expr );
     674                return create_resume_rethrow( throwStmt );
     675        }
     676
     677        void translateThrows( std::list< Declaration *> & translationUnit ) {
     678                PassVisitor<ThrowMutatorCore> translator;
    692679                mutateAll( translationUnit, translator );
    693680        }
     681
     682        void translateTries( std::list< Declaration *> & translationUnit ) {
     683                PassVisitor<TryMutatorCore> translator;
     684                mutateAll( translationUnit, translator );
     685        }
    694686}
  • src/ControlStruct/ExceptTranslate.h

    r3c64c668 r58fe85a  
    99// Author           : Andrew Beach
    1010// Created On       : Tus Jun 06 10:13:00 2017
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Jul 22 09:19:23 2017
    13 // Update Count     : 4
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tus May 19 11:47:00 2020
     13// Update Count     : 5
    1414//
    1515
     
    2121
    2222namespace ControlStruct {
    23         void translateEHM( std::list< Declaration *> & translationUnit );
    24         // Converts exception handling structures into their underlying C code.  Translation does use the exception
    25         // handling header, make sure it is visible wherever translation occurs.
     23        void translateThrows( std::list< Declaration *> & translationUnit );
     24        /* Replaces all throw & throwResume statements with function calls.
     25         * These still need to be resolved, so call this before the reslover.
     26         */
     27
     28        void translateTries( std::list< Declaration *> & translationUnit );
     29        /* Replaces all try blocks (and their many clauses) with function definitions and calls.
     30         * This uses the exception built-ins to produce typed output and should take place after
     31         * the resolver. It also produces virtual casts and should happen before they are expanded.
     32         */
    2633}
    2734
  • src/ControlStruct/module.mk

    r3c64c668 r58fe85a  
    1717SRC_CONTROLSTRUCT = \
    1818        ControlStruct/ForExprMutator.cc \
     19        ControlStruct/ForExprMutator.h \
    1920        ControlStruct/LabelFixer.cc \
     21        ControlStruct/LabelFixer.h \
    2022        ControlStruct/LabelGenerator.cc \
     23        ControlStruct/LabelGenerator.h \
    2124        ControlStruct/MLEMutator.cc \
    22         ControlStruct/Mutate.cc
     25        ControlStruct/MLEMutator.h \
     26        ControlStruct/Mutate.cc \
     27        ControlStruct/Mutate.h
    2328
    24 SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc
     29SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc ControlStruct/ExceptTranslate.h
    2530SRCDEMANGLE += $(SRC_CONTROLSTRUCT)
    2631
  • src/GenPoly/GenPoly.cc

    r3c64c668 r58fe85a  
    4646                }
    4747
     48                bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const ast::TypeSubstitution * env) {
     49                        for (auto &param : params) {
     50                                auto paramType = param.strict_as<ast::TypeExpr>();
     51                                if (isPolyType(paramType->type, env)) return true;
     52                        }
     53                        return false;
     54                }
     55
    4856                /// Checks a parameter list for polymorphic parameters from tyVars; will substitute according to env if present
    4957                bool hasPolyParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) {
     
    5664                }
    5765
     66                bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const TyVarMap & tyVars, const ast::TypeSubstitution * env) {
     67                        for (auto &param : params) {
     68                                auto paramType = param.strict_as<ast::TypeExpr>();
     69                                if (isPolyType(paramType->type, tyVars, env)) return true;
     70                        }
     71                        return false;
     72                }
     73
    5874                /// Checks a parameter list for dynamic-layout parameters from tyVars; will substitute according to env if present
    5975                bool hasDynParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) {
     
    92108                        Type *newType = env->lookup( typeInst->get_name() );
    93109                        if ( newType ) return newType;
     110                }
     111                return type;
     112        }
     113
     114        const ast::Type * replaceTypeInst(const ast::Type * type, const ast::TypeSubstitution * env) {
     115                if (!env) return type;
     116                if (auto typeInst = dynamic_cast<const ast::TypeInstType*> (type)) {
     117                        auto newType = env->lookup(typeInst);
     118                        if (newType) return newType;
    94119                }
    95120                return type;
     
    111136        }
    112137
     138        const ast::Type * isPolyType(const ast::Type * type, const ast::TypeSubstitution * env) {
     139                type = replaceTypeInst( type, env );
     140
     141                if ( dynamic_cast< const ast::TypeInstType * >( type ) ) {
     142                        return type;
     143                } else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
     144                        return isPolyType( arrayType->base, env );
     145                } else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {
     146                        if ( hasPolyParams( structType->params, env ) ) return type;
     147                } else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {
     148                        if ( hasPolyParams( unionType->params, env ) ) return type;
     149                }
     150                return 0;
     151        }
     152
    113153        Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env ) {
    114154                type = replaceTypeInst( type, env );
     
    126166                }
    127167                return 0;
     168        }
     169
     170        const ast::Type * isPolyType(const ast::Type * type, const TyVarMap & tyVars, const ast::TypeSubstitution * env) {
     171                type = replaceTypeInst( type, env );
     172
     173                if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( type ) ) {
     174                        return tyVars.find(typeInst->typeString()) != tyVars.end() ? type : nullptr;
     175                } else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
     176                        return isPolyType( arrayType->base, env );
     177                } else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {
     178                        if ( hasPolyParams( structType->params, env ) ) return type;
     179                } else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {
     180                        if ( hasPolyParams( unionType->params, env ) ) return type;
     181                }
     182                return nullptr;
    128183        }
    129184
     
    449504        }
    450505
     506        namespace {
     507                // temporary hack to avoid re-implementing anything related to TyVarMap
     508                // does this work? these two structs have identical definitions.
     509                inline TypeDecl::Data convData(const ast::TypeDecl::Data & data) {
     510                        return *reinterpret_cast<const TypeDecl::Data *>(&data);
     511                }
     512        }
     513
    451514        bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env ) {
    452515                // is parameter is not polymorphic, don't need to box
     
    459522        }
    460523
     524        bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env) {
     525                // is parameter is not polymorphic, don't need to box
     526                if ( ! isPolyType( param, exprTyVars ) ) return false;
     527                ast::ptr<ast::Type> newType = arg;
     528                if ( env ) env->apply( newType );
     529                // if the argument's type is polymorphic, we don't need to box again!
     530                return ! isPolyType( newType );
     531        }
     532
    461533        bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env ) {
    462534                FunctionType * function = getFunctionType( appExpr->function->result );
     
    467539        }
    468540
     541        bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * appExpr, const ast::TypeSubstitution * env) {
     542                const ast::FunctionType * function = getFunctionType(appExpr->func->result);
     543                assertf( function, "ApplicationExpr has non-function type: %s", toString( appExpr->func->result ).c_str() );
     544                TyVarMap exprTyVars(TypeDecl::Data{});
     545                makeTyVarMap(function, exprTyVars);
     546                return needsBoxing(param, arg, exprTyVars, env);
     547
     548        }
     549
    469550        void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap ) {
    470551                tyVarMap.insert( tyVar->name, TypeDecl::Data{ tyVar } );
     552        }
     553
     554        void addToTyVarMap( const ast::TypeInstType * tyVar, TyVarMap & tyVarMap) {
     555                tyVarMap.insert(tyVar->typeString(), convData(ast::TypeDecl::Data{tyVar->base}));
    471556        }
    472557
     
    478563                if ( PointerType *pointer = dynamic_cast< PointerType* >( type ) ) {
    479564                        makeTyVarMap( pointer->get_base(), tyVarMap );
     565                }
     566        }
     567
     568        void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap) {
     569                if (auto ptype = dynamic_cast<const ast::FunctionType *>(type)) {
     570                        for (auto & tyVar : ptype->forall) {
     571                                assert (tyVar);
     572                                addToTyVarMap(tyVar, tyVarMap);
     573                        }
     574                }
     575                if (auto pointer = dynamic_cast<const ast::PointerType *>(type)) {
     576                        makeTyVarMap(pointer->base, tyVarMap);
    480577                }
    481578        }
  • src/GenPoly/GenPoly.h

    r3c64c668 r58fe85a  
    2626
    2727namespace GenPoly {
     28
    2829        typedef ErasableScopedMap< std::string, TypeDecl::Data > TyVarMap;
    29 
    3030        /// Replaces a TypeInstType by its referrent in the environment, if applicable
    3131        Type* replaceTypeInst( Type* type, const TypeSubstitution* env );
     
    3333        /// returns polymorphic type if is polymorphic type, NULL otherwise; will look up substitution in env if provided
    3434        Type *isPolyType( Type *type, const TypeSubstitution *env = 0 );
     35        const ast::Type * isPolyType(const ast::Type * type, const ast::TypeSubstitution * env = nullptr);
    3536
    3637        /// returns polymorphic type if is polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided
    3738        Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env = 0 );
     39        const ast::Type * isPolyType(const ast::Type * type, const TyVarMap & tyVars, const ast::TypeSubstitution * env = nullptr);
    3840
    3941        /// returns dynamic-layout type if is dynamic-layout type in tyVars, NULL otherwise; will look up substitution in env if provided
     
    8486        /// true if arg requires boxing given exprTyVars
    8587        bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env );
     88        bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env);
    8689
    8790        /// true if arg requires boxing in the call to appExpr
    8891        bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env );
     92        bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * appExpr, const ast::TypeSubstitution * env);
    8993
    9094        /// Adds the type variable `tyVar` to `tyVarMap`
     
    9397        /// Adds the declarations in the forall list of type (and its pointed-to type if it's a pointer type) to `tyVarMap`
    9498        void makeTyVarMap( Type *type, TyVarMap &tyVarMap );
     99        void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap);
    95100
    96101        /// Prints type variable map
  • src/GenPoly/InstantiateGeneric.cc

    r3c64c668 r58fe85a  
    99// Author           : Aaron B. Moss
    1010// Created On       : Thu Aug 04 18:33:00 2016
    11 // Last Modified By : Aaron B. Moss
    12 // Last Modified On : Thu Aug 04 18:33:00 2016
    13 // Update Count     : 1
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Wed Jul 16 10:17:00 2020
     13// Update Count     : 2
    1414//
    1515#include "InstantiateGeneric.h"
     
    172172                InstantiationMap< AggregateDecl, AggregateDecl > instantiations;
    173173                /// Set of types which are dtype-only generic (and therefore have static layout)
    174                 ScopedSet< AggregateDecl* > dtypeStatics;
     174                std::set<AggregateDecl *> dtypeStatics;
    175175                /// Namer for concrete types
    176176                UniqueName typeNamer;
     
    297297        }
    298298
     299        template< typename AggrInst >
     300        static AggrInst * asForward( AggrInst * decl ) {
     301                if ( !decl->body ) {
     302                        return nullptr;
     303                }
     304                decl = decl->clone();
     305                decl->body = false;
     306                deleteAll( decl->members );
     307                decl->members.clear();
     308                return decl;
     309        }
     310
    299311        void GenericInstantiator::stripDtypeParams( AggregateDecl *base, std::list< TypeDecl* >& baseParams, const std::list< TypeExpr* >& typeSubs ) {
    300312                substituteMembers( base->get_members(), baseParams, typeSubs );
     
    373385                                concDecl->set_body( inst->get_baseStruct()->has_body() );
    374386                                substituteMembers( inst->get_baseStruct()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() );
    375                                 insert( inst, typeSubs, concDecl ); // must insert before recursion
     387                                // Forward declare before recursion. (TODO: Only when needed, #199.)
     388                                insert( inst, typeSubs, concDecl );
     389                                if ( StructDecl *forwardDecl = asForward( concDecl ) ) {
     390                                        declsToAddBefore.push_back( forwardDecl );
     391                                }
    376392                                concDecl->acceptMutator( *visitor ); // recursively instantiate members
    377393                                declsToAddBefore.push_back( concDecl ); // must occur before declaration is added so that member instantiations appear first
     
    423439                                concDecl->set_body( inst->get_baseUnion()->has_body() );
    424440                                substituteMembers( inst->get_baseUnion()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() );
    425                                 insert( inst, typeSubs, concDecl ); // must insert before recursion
     441                                // Forward declare before recursion. (TODO: Only when needed, #199.)
     442                                insert( inst, typeSubs, concDecl );
     443                                if ( UnionDecl *forwardDecl = asForward( concDecl ) ) {
     444                                        declsToAddBefore.push_back( forwardDecl );
     445                                }
    426446                                concDecl->acceptMutator( *visitor ); // recursively instantiate members
    427447                                declsToAddBefore.push_back( concDecl ); // must occur before declaration is added so that member instantiations appear first
     
    485505        void GenericInstantiator::beginScope() {
    486506                instantiations.beginScope();
    487                 dtypeStatics.beginScope();
    488507        }
    489508
    490509        void GenericInstantiator::endScope() {
    491510                instantiations.endScope();
    492                 dtypeStatics.endScope();
    493511        }
    494512
  • src/GenPoly/Specialize.cc

    r3c64c668 r58fe85a  
    99// Author           : Richard C. Bilson
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Dec 13 23:40:49 2019
    13 // Update Count     : 32
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Thr Jul  2 17:42:00 2020
     13// Update Count     : 33
    1414//
    1515
     
    4242
    4343namespace GenPoly {
    44         struct Specialize final : public WithConstTypeSubstitution, public WithStmtsToAdd, public WithVisitorRef<Specialize> {
     44        struct Specialize final : public WithConstTypeSubstitution,
     45                        public WithDeclsToAdd, public WithVisitorRef<Specialize> {
    4546                Expression * postmutate( ApplicationExpr *applicationExpr );
    4647                Expression * postmutate( CastExpr *castExpr );
     
    217218                thunkFunc->get_attributes().push_back( new Attribute( "unused" ) );
    218219
     220                // Thunks at the global level must be static to avoid collisions between files.
     221                // (Conversly thunks inside a function must be unique and not static.)
     222                thunkFunc->storageClasses.is_static = !isInFunction();
     223
    219224                // thread thunk parameters into call to actual function, naming thunk parameters as we go
    220225                UniqueName paramNamer( paramPrefix );
     
    248253                } // if
    249254
    250                 // handle any specializations that may still be present
    251                 std::string oldParamPrefix = paramPrefix;
    252                 paramPrefix += "p";
    253                 // save stmtsToAddBefore in oldStmts
    254                 std::list< Statement* > oldStmts;
    255                 oldStmts.splice( oldStmts.end(), stmtsToAddBefore );
    256                 appExpr->acceptMutator( *visitor );
    257                 paramPrefix = oldParamPrefix;
    258                 // write any statements added for recursive specializations into the thunk body
    259                 thunkFunc->statements->kids.splice( thunkFunc->statements->kids.end(), stmtsToAddBefore );
    260                 // restore oldStmts into stmtsToAddBefore
    261                 stmtsToAddBefore.splice( stmtsToAddBefore.end(), oldStmts );
     255                // Handle any specializations that may still be present.
     256                {
     257                        std::string oldParamPrefix = paramPrefix;
     258                        paramPrefix += "p";
     259                        std::list< Declaration * > oldDecls;
     260                        oldDecls.splice( oldDecls.end(), declsToAddBefore );
     261
     262                        appExpr->acceptMutator( *visitor );
     263                        // Write recursive specializations into the thunk body.
     264                        for ( Declaration * decl : declsToAddBefore ) {
     265                                thunkFunc->statements->kids.push_back( new DeclStmt( decl ) );
     266                        }
     267
     268                        declsToAddBefore = std::move( oldDecls );
     269                        paramPrefix = oldParamPrefix;
     270                }
    262271
    263272                // add return (or valueless expression) to the thunk
     
    270279                thunkFunc->statements->kids.push_back( appStmt );
    271280
    272                 // add thunk definition to queue of statements to add
    273                 stmtsToAddBefore.push_back( new DeclStmt( thunkFunc ) );
     281                // Add the thunk definition (converted to DeclStmt if appproprate).
     282                declsToAddBefore.push_back( thunkFunc );
    274283                // return address of thunk function as replacement expression
    275284                return new AddressExpr( new VariableExpr( thunkFunc ) );
  • src/GenPoly/module.mk

    r3c64c668 r58fe85a  
    1616
    1717SRC += GenPoly/Box.cc \
     18       GenPoly/Box.h \
     19       GenPoly/ErasableScopedMap.h \
     20       GenPoly/FindFunction.cc \
     21       GenPoly/FindFunction.h \
    1822       GenPoly/GenPoly.cc \
     23       GenPoly/GenPoly.h \
     24       GenPoly/InstantiateGeneric.cc \
     25       GenPoly/InstantiateGeneric.h \
     26       GenPoly/Lvalue.cc \
     27       GenPoly/Lvalue.h \
     28       GenPoly/ScopedSet.h \
    1929       GenPoly/ScrubTyVars.cc \
    20        GenPoly/Lvalue.cc \
     30       GenPoly/ScrubTyVars.h \
    2131       GenPoly/Specialize.cc \
    22        GenPoly/FindFunction.cc \
    23        GenPoly/InstantiateGeneric.cc
     32       GenPoly/Specialize.h
    2433
    25 SRCDEMANGLE += GenPoly/GenPoly.cc GenPoly/Lvalue.cc
     34SRCDEMANGLE += GenPoly/GenPoly.cc GenPoly/GenPoly.h GenPoly/Lvalue.cc GenPoly/Lvalue.h
    2635
  • src/InitTweak/FixGlobalInit.cc

    r3c64c668 r58fe85a  
    3434#include "SynTree/Visitor.h"       // for acceptAll, Visitor
    3535
     36#include "AST/Expr.hpp"
     37#include "AST/Node.hpp"
     38#include "AST/Pass.hpp"
     39
    3640namespace InitTweak {
    3741        class GlobalFixer : public WithShortCircuiting {
     
    5054                FunctionDecl * initFunction;
    5155                FunctionDecl * destroyFunction;
     56        };
     57
     58        class GlobalFixer_new : public ast::WithShortCircuiting {
     59        public:
     60                void previsit (const ast::ObjectDecl *);
     61                void previsit (const ast::FunctionDecl *) { visit_children = false; }
     62                void previsit (const ast::StructDecl *) { visit_children = false; }
     63                void previsit (const ast::UnionDecl *) { visit_children = false; }
     64                void previsit (const ast::EnumDecl *) { visit_children = false; }
     65                void previsit (const ast::TraitDecl *) { visit_children = false; }
     66                void previsit (const ast::TypeDecl *) { visit_children = false; }
     67
     68                std::list< ast::ptr<ast::Stmt> > initStmts;
     69                std::list< ast::ptr<ast::Stmt> > destroyStmts;
    5270        };
    5371
     
    91109        }
    92110
     111        void fixGlobalInit(ast::TranslationUnit & translationUnit, bool inLibrary) {
     112                ast::Pass<GlobalFixer_new> fixer;
     113                accept_all(translationUnit, fixer);
     114
     115                if ( !fixer.core.initStmts.empty() ) {
     116                        std::vector<ast::ptr<ast::Expr>> 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))});
     120
     121                        translationUnit.decls.emplace_back( initFunction );
     122                } // if
     123
     124                if ( !fixer.core.destroyStmts.empty() ) {
     125                        std::vector<ast::ptr<ast::Expr>> 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))});
     129
     130                        translationUnit.decls.emplace_back(destroyFunction);
     131                } // if
     132        }
     133
    93134        void GlobalFixer::previsit( ObjectDecl *objDecl ) {
    94135                std::list< Statement * > & initStatements = initFunction->get_statements()->get_kids();
     
    112153                        } // if
    113154                        if ( Statement * ctor = ctorInit->ctor ) {
     155                                addDataSectonAttribute( objDecl );
    114156                                initStatements.push_back( ctor );
    115157                                objDecl->init = nullptr;
     
    126168        }
    127169
     170        void GlobalFixer_new::previsit(const ast::ObjectDecl * objDecl) {
     171                auto mutDecl = mutate(objDecl);
     172                assertf(mutDecl == objDecl, "Global object decl must be unique");
     173                if ( auto ctorInit = objDecl->init.as<ast::ConstructorInit>() ) {
     174                        // a decision should have been made by the resolver, so ctor and init are not both non-NULL
     175                        assert( ! ctorInit->ctor || ! ctorInit->init );
     176
     177                        const ast::Stmt * dtor = ctorInit->dtor;
     178                        if ( dtor && ! isIntrinsicSingleArgCallStmt( dtor ) ) {
     179                                // don't need to call intrinsic dtor, because it does nothing, but
     180                                // non-intrinsic dtors must be called
     181                                destroyStmts.push_front( dtor );
     182                                // ctorInit->dtor = nullptr;
     183                        } // if
     184                        if ( const ast::Stmt * ctor = ctorInit->ctor ) {
     185                                addDataSectionAttribute(mutDecl);
     186                                initStmts.push_back( ctor );
     187                                mutDecl->init = nullptr;
     188                                // ctorInit->ctor = nullptr;
     189                        } else if ( const ast::Init * init = ctorInit->init ) {
     190                                mutDecl->init = init;
     191                                // ctorInit->init = nullptr;
     192                        } else {
     193                                // no constructor and no initializer, which is okay
     194                                mutDecl->init = nullptr;
     195                        } // if
     196                        // delete ctorInit;
     197                } // if
     198        }
     199
    128200        // only modify global variables
    129201        void GlobalFixer::previsit( FunctionDecl * ) { visit_children = false; }
  • src/InitTweak/FixGlobalInit.h

    r3c64c668 r58fe85a  
    1919#include <string>  // for string
    2020
     21#include <AST/Fwd.hpp>
     22
     23
    2124class Declaration;
    2225
     
    2629        /// function is for library code.
    2730        void fixGlobalInit( std::list< Declaration * > & translationUnit, bool inLibrary );
     31        void fixGlobalInit( ast::TranslationUnit & translationUnit, bool inLibrary );
    2832} // namespace
    2933
  • src/InitTweak/FixInit.cc

    r3c64c668 r58fe85a  
    219219                };
    220220
    221                 struct SplitExpressions : public WithShortCircuiting, public WithTypeSubstitution, public WithStmtsToAdd {
     221                struct SplitExpressions : public WithShortCircuiting, /*public WithTypeSubstitution, */public WithStmtsToAdd {
    222222                        /// add CompoundStmts around top-level expressions so that temporaries are destroyed in the correct places.
    223223                        static void split( std::list< Declaration * > &translationUnit );
     
    802802                                if ( Statement * ctor = ctorInit->get_ctor() ) {
    803803                                        if ( objDecl->get_storageClasses().is_static ) {
     804
     805                                                // The ojbect needs to go in the data section, regardless of dtor complexity below.
     806                                                // The attribute works, and is meant to apply, both for leaving the static local alone,
     807                                                // and for hoisting it out as a static global.
     808                                                addDataSectonAttribute( objDecl );
     809
    804810                                                // originally wanted to take advantage of gcc nested functions, but
    805811                                                // we get memory errors with this approach. To remedy this, the static
  • src/InitTweak/FixInit.h

    r3c64c668 r58fe85a  
    2020
    2121class Declaration;
     22namespace ast {
     23        struct TranslationUnit;
     24}
    2225
    2326namespace InitTweak {
    2427        /// replace constructor initializers with expression statements and unwrap basic C-style initializers
    2528        void fix( std::list< Declaration * > & translationUnit, bool inLibrary );
     29
     30        void fix( ast::TranslationUnit & translationUnit, bool inLibrary);
    2631} // namespace
    2732
  • src/InitTweak/GenInit.cc

    r3c64c668 r58fe85a  
    2626#include "AST/Node.hpp"
    2727#include "AST/Stmt.hpp"
     28#include "CompilationState.h"
    2829#include "CodeGen/OperatorTable.h"
    2930#include "Common/PassVisitor.h"        // for PassVisitor, WithGuards, WithShort...
     
    121122        };
    122123
     124        struct HoistArrayDimension_NoResolve final : public WithDeclsToAdd, public WithShortCircuiting, public WithGuards {
     125                /// hoist dimension from array types in object declaration so that it uses a single
     126                /// const variable of type size_t, so that side effecting array dimensions are only
     127                /// computed once.
     128                static void hoistArrayDimension( std::list< Declaration * > & translationUnit );
     129
     130                void premutate( ObjectDecl * objectDecl );
     131                DeclarationWithType * postmutate( ObjectDecl * objectDecl );
     132                void premutate( FunctionDecl *functionDecl );
     133                // should not traverse into any of these declarations to find objects
     134                // that need to be constructed or destructed
     135                void premutate( AggregateDecl * ) { visit_children = false; }
     136                void premutate( NamedTypeDecl * ) { visit_children = false; }
     137                void premutate( FunctionType * ) { visit_children = false; }
     138
     139                void hoist( Type * type );
     140
     141                Type::StorageClasses storageClasses;
     142                bool inFunction = false;
     143        };
     144
    123145        void genInit( std::list< Declaration * > & translationUnit ) {
     146                if (!useNewAST) {
     147                        HoistArrayDimension::hoistArrayDimension( translationUnit );
     148                }
     149                else {
     150                        HoistArrayDimension_NoResolve::hoistArrayDimension( translationUnit );
     151                }
    124152                fixReturnStatements( translationUnit );
    125                 HoistArrayDimension::hoistArrayDimension( translationUnit );
    126                 CtorDtor::generateCtorDtor( translationUnit );
     153
     154                if (!useNewAST) {
     155                        CtorDtor::generateCtorDtor( translationUnit );
     156                }
    127157        }
    128158
     
    196226                        arrayType->isVarLen = ! isConstExpr( arrayType->dimension );
    197227                        // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects.
     228                        // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve
     229                        // still try to detect constant expressions
    198230                        if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return;
    199231
     
    210242
    211243        void HoistArrayDimension::premutate( FunctionDecl * ) {
     244                GuardValue( inFunction );
     245                inFunction = true;
     246        }
     247
     248        // precompute array dimension expression, because constructor generation may duplicate it,
     249        // which would be incorrect if it is a side-effecting computation.
     250        void HoistArrayDimension_NoResolve::hoistArrayDimension( std::list< Declaration * > & translationUnit ) {
     251                PassVisitor<HoistArrayDimension_NoResolve> hoister;
     252                mutateAll( translationUnit, hoister );
     253        }
     254
     255        void HoistArrayDimension_NoResolve::premutate( ObjectDecl * objectDecl ) {
     256                GuardValue( storageClasses );
     257                storageClasses = objectDecl->get_storageClasses();
     258        }
     259
     260        DeclarationWithType * HoistArrayDimension_NoResolve::postmutate( ObjectDecl * objectDecl ) {
     261                hoist( objectDecl->get_type() );
     262                return objectDecl;
     263        }
     264
     265        void HoistArrayDimension_NoResolve::hoist( Type * type ) {
     266                // if in function, generate const size_t var
     267                static UniqueName dimensionName( "_array_dim" );
     268
     269                // C doesn't allow variable sized arrays at global scope or for static variables, so don't hoist dimension.
     270                if ( ! inFunction ) return;
     271                if ( storageClasses.is_static ) return;
     272
     273                if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) {
     274                        if ( ! arrayType->get_dimension() ) return; // xxx - recursive call to hoist?
     275                        // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects.
     276                        // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve
     277                        // still try to detect constant expressions
     278                        if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return;
     279
     280                        ObjectDecl * arrayDimension = new ObjectDecl( dimensionName.newName(), storageClasses, LinkageSpec::C, 0, Validate::SizeType->clone(), new SingleInit( arrayType->get_dimension() ) );
     281                        arrayDimension->get_type()->set_const( true );
     282
     283                        arrayType->set_dimension( new VariableExpr( arrayDimension ) );
     284                        declsToAddBefore.push_back( arrayDimension );
     285
     286                        hoist( arrayType->get_base() );
     287                        return;
     288                }
     289        }
     290
     291        void HoistArrayDimension_NoResolve::premutate( FunctionDecl * ) {
    212292                GuardValue( inFunction );
    213293                inFunction = true;
     
    245325        }
    246326
     327        // why is this not just on FunctionDecl?
    247328        void ManagedTypes::handleDWT( DeclarationWithType * dwt ) {
    248329                // if this function is a user-defined constructor or destructor, mark down the type as "managed"
     
    275356        void ManagedTypes::endScope() { managedTypes.endScope(); }
    276357
     358        bool ManagedTypes_new::isManaged( const ast::Type * type ) const {
     359                // references are never constructed
     360                if ( dynamic_cast< const ast::ReferenceType * >( type ) ) return false;
     361                if ( auto tupleType = dynamic_cast< const ast::TupleType * > ( type ) ) {
     362                        // tuple is also managed if any of its components are managed
     363                        for (auto & component : tupleType->types) {
     364                                if (isManaged(component)) return true;
     365                        }
     366                }
     367                // need to clear and reset qualifiers when determining if a type is managed
     368                // ValueGuard< Type::Qualifiers > qualifiers( type->get_qualifiers() );
     369                auto tmp = shallowCopy(type);
     370                tmp->qualifiers = {};
     371                // delete tmp at return
     372                ast::ptr<ast::Type> guard = tmp;
     373                // a type is managed if it appears in the map of known managed types, or if it contains any polymorphism (is a type variable or generic type containing a type variable)
     374                return managedTypes.find( Mangle::mangle( tmp, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) ) != managedTypes.end() || GenPoly::isPolyType( tmp );
     375        }
     376
     377        bool ManagedTypes_new::isManaged( const ast::ObjectDecl * objDecl ) const {
     378                const ast::Type * type = objDecl->type;
     379                while ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) {
     380                        // must always construct VLAs with an initializer, since this is an error in C
     381                        if ( at->isVarLen && objDecl->init ) return true;
     382                        type = at->base;
     383                }
     384                return isManaged( type );
     385        }
     386
     387        void ManagedTypes_new::handleDWT( const ast::DeclWithType * dwt ) {
     388                // if this function is a user-defined constructor or destructor, mark down the type as "managed"
     389                if ( ! dwt->linkage.is_overrideable && CodeGen::isCtorDtor( dwt->name ) ) {
     390                        auto & params = GenPoly::getFunctionType( dwt->get_type())->params;
     391                        assert( ! params.empty() );
     392                        // Type * type = InitTweak::getPointerBase( params.front() );
     393                        // assert( type );
     394                        managedTypes.insert( Mangle::mangle( params.front(), {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );
     395                }
     396        }
     397
     398        void ManagedTypes_new::handleStruct( const ast::StructDecl * aggregateDecl ) {
     399                // don't construct members, but need to take note if there is a managed member,
     400                // because that means that this type is also managed
     401                for ( auto & member : aggregateDecl->members ) {
     402                        if ( auto field = member.as<ast::ObjectDecl>() ) {
     403                                if ( isManaged( field ) ) {
     404                                        // generic parameters should not play a role in determining whether a generic type is constructed - construct all generic types, so that
     405                                        // polymorphic constructors make generic types managed types
     406                                        ast::StructInstType inst( aggregateDecl );
     407                                        managedTypes.insert( Mangle::mangle( &inst, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );
     408                                        break;
     409                                }
     410                        }
     411                }
     412        }
     413
     414        void ManagedTypes_new::beginScope() { managedTypes.beginScope(); }
     415        void ManagedTypes_new::endScope() { managedTypes.endScope(); }
     416
    277417        ImplicitCtorDtorStmt * genCtorDtor( const std::string & fname, ObjectDecl * objDecl, Expression * arg ) {
    278418                // call into genImplicitCall from Autogen.h to generate calls to ctor/dtor
     
    283423                assert( stmts.size() <= 1 );
    284424                return stmts.size() == 1 ? strict_dynamic_cast< ImplicitCtorDtorStmt * >( stmts.front() ) : nullptr;
     425
     426        }
     427
     428        ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg) {
     429                assertf(objDecl, "genCtorDtor passed null objDecl");
     430                InitExpander_new srcParam(arg);
     431                return SymTab::genImplicitCall(srcParam, new ast::VariableExpr(loc, objDecl), loc, fname, objDecl);
    285432        }
    286433
     
    363510        // constructable object
    364511        InitExpander_new srcParam{ objDecl->init }, nullParam{ (const ast::Init *)nullptr };
     512        ast::ptr< ast::Expr > dstParam = new ast::VariableExpr(loc, objDecl);
    365513       
    366514        ast::ptr< ast::Stmt > ctor = SymTab::genImplicitCall(
    367                 srcParam, new ast::VariableExpr{ loc, objDecl }, loc, "?{}", objDecl );
     515                srcParam, dstParam, loc, "?{}", objDecl );
    368516        ast::ptr< ast::Stmt > dtor = SymTab::genImplicitCall(
    369                 nullParam, new ast::VariableExpr{ loc, objDecl }, loc, "^?{}", objDecl,
     517                nullParam, dstParam, loc, "^?{}", objDecl,
    370518                SymTab::LoopBackward );
    371519       
  • src/InitTweak/GenInit.h

    r3c64c668 r58fe85a  
    3333        /// generates a single ctor/dtor statement using objDecl as the 'this' parameter and arg as the optional argument
    3434        ImplicitCtorDtorStmt * genCtorDtor( const std::string & fname, ObjectDecl * objDecl, Expression * arg = nullptr );
     35        ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr);
    3536
    3637        /// creates an appropriate ConstructorInit node which contains a constructor, destructor, and C-initializer
     
    5152                GenPoly::ScopedSet< std::string > managedTypes;
    5253        };
     54
     55        class ManagedTypes_new {
     56        public:
     57                bool isManaged( const ast::ObjectDecl * objDecl ) const ; // determine if object is managed
     58                bool isManaged( const ast::Type * type ) const; // determine if type is managed
     59
     60                void handleDWT( const ast::DeclWithType * dwt ); // add type to managed if ctor/dtor
     61                void handleStruct( const ast::StructDecl * aggregateDecl ); // add type to managed if child is managed
     62
     63                void beginScope();
     64                void endScope();
     65        private:
     66                GenPoly::ScopedSet< std::string > managedTypes;
     67        };
    5368} // namespace
    5469
  • src/InitTweak/InitTweak.cc

    r3c64c668 r58fe85a  
    8787                };
    8888
     89                struct HasDesignations_new : public ast::WithShortCircuiting {
     90                        bool result = false;
     91
     92                        void previsit( const ast::Node * ) {
     93                                // short circuit if we already know there are designations
     94                                if ( result ) visit_children = false;
     95                        }
     96
     97                        void previsit( const ast::Designation * des ) {
     98                                // short circuit if we already know there are designations
     99                                if ( result ) visit_children = false;
     100                                else if ( ! des->designators.empty() ) {
     101                                        result = true;
     102                                        visit_children = false;
     103                                }
     104                        }
     105                };
     106
     107                struct InitDepthChecker_new : public ast::WithGuards {
     108                        bool result = true;
     109                        const ast::Type * type;
     110                        int curDepth = 0, maxDepth = 0;
     111                        InitDepthChecker_new( const ast::Type * type ) : type( type ) {
     112                                const ast::Type * t = type;
     113                                while ( auto at = dynamic_cast< const ast::ArrayType * >( t ) ) {
     114                                        maxDepth++;
     115                                        t = at->base;
     116                                }
     117                                maxDepth++;
     118                        }
     119                        void previsit( ListInit * ) {
     120                                curDepth++;
     121                                GuardAction( [this]() { curDepth--; } );
     122                                if ( curDepth > maxDepth ) result = false;
     123                        }
     124                };
     125
    89126                struct InitFlattener_old : public WithShortCircuiting {
    90127                        void previsit( SingleInit * singleInit ) {
     
    124161        }
    125162
     163        bool isDesignated( const ast::Init * init ) {
     164                ast::Pass<HasDesignations_new> finder;
     165                maybe_accept( init, finder );
     166                return finder.core.result;
     167        }
     168
     169        bool checkInitDepth( const ast::ObjectDecl * objDecl ) {
     170                ast::Pass<InitDepthChecker_new> checker( objDecl->type );
     171                maybe_accept( objDecl->init.get(), checker );
     172                return checker.core.result;
     173        }
     174
    126175std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init ) {
    127176        ast::Pass< InitFlattener_new > flattener;
    128177        maybe_accept( init, flattener );
    129         return std::move( flattener.pass.argList );
     178        return std::move( flattener.core.argList );
    130179}
    131180
     
    358407                        if ( auto listInit = dynamic_cast< const ast::ListInit * >( init ) ) {
    359408                                for ( const ast::Init * init : *listInit ) {
    360                                         buildCallExpr( callExpr, index, dimension, init, out );
     409                                        buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
    361410                                }
    362411                        } else {
    363                                 buildCallExpr( callExpr, index, dimension, init, out );
     412                                buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
    364413                        }
    365414                } else {
     
    498547        }
    499548
     549        const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func) {
     550                assertf( func, "getParamThis: nullptr ftype" );
     551                auto & params = func->params;
     552                assertf( ! params.empty(), "getParamThis: ftype with 0 parameters: %s", toString( func ).c_str());
     553                return params.front().strict_as<ast::ObjectDecl>();
     554        }
     555
    500556        bool tryConstruct( DeclarationWithType * dwt ) {
    501557                ObjectDecl * objDecl = dynamic_cast< ObjectDecl * >( dwt );
     
    511567        }
    512568
     569        bool tryConstruct( const ast::DeclWithType * dwt ) {
     570                auto objDecl = dynamic_cast< const ast::ObjectDecl * >( dwt );
     571                if ( ! objDecl ) return false;
     572                return (objDecl->init == nullptr ||
     573                                ( objDecl->init != nullptr && objDecl->init->maybeConstructed ))
     574                        && ! objDecl->storage.is_extern
     575                        && isConstructable( objDecl->type );
     576        }
     577
     578        bool isConstructable( const ast::Type * type ) {
     579                return ! dynamic_cast< const ast::VarArgsType * >( type ) && ! dynamic_cast< const ast::ReferenceType * >( type )
     580                && ! dynamic_cast< const ast::FunctionType * >( type ) && ! Tuples::isTtype( type );
     581        }
     582
    513583        struct CallFinder_old {
    514584                CallFinder_old( const std::list< std::string > & names ) : names( names ) {}
     
    536606
    537607        struct CallFinder_new final {
    538                 std::vector< ast::ptr< ast::Expr > > matches;
     608                std::vector< const ast::Expr * > matches;
    539609                const std::vector< std::string > names;
    540610
     
    558628        }
    559629
    560         std::vector< ast::ptr< ast::Expr > > collectCtorDtorCalls( const ast::Stmt * stmt ) {
     630        std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ) {
    561631                ast::Pass< CallFinder_new > finder{ std::vector< std::string >{ "?{}", "^?{}" } };
    562632                maybe_accept( stmt, finder );
    563                 return std::move( finder.pass.matches );
     633                return std::move( finder.core.matches );
    564634        }
    565635
     
    696766                template <typename Predicate>
    697767                bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) {
    698                         std::vector< ast::ptr< ast::Expr > > callExprs = collectCtorDtorCalls( stmt );
     768                        std::vector< const ast::Expr * > callExprs = collectCtorDtorCalls( stmt );
    699769                        return std::all_of( callExprs.begin(), callExprs.end(), pred );
    700770                }
     
    9391009        }
    9401010
     1011        // looks like some other such codegen uses UntypedExpr and does not create fake function. should revisit afterwards
     1012        // following passes may accidentally resolve this expression if returned as untyped...
     1013        ast::Expr * createBitwiseAssignment (const ast::Expr * dst, const ast::Expr * src) {
     1014                static ast::ptr<ast::FunctionDecl> assign = nullptr;
     1015                if (!assign) {
     1016                        auto td = new ast::TypeDecl({}, "T", {}, nullptr, ast::TypeDecl::Dtype, true);
     1017                        assign = new ast::FunctionDecl({}, "?=?", {},
     1018                        { new ast::ObjectDecl({}, "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))),
     1019                          new ast::ObjectDecl({}, "_src", new ast::TypeInstType("T", td))},
     1020                        { new ast::ObjectDecl({}, "_ret", new ast::TypeInstType("T", td))}, nullptr, {}, ast::Linkage::Intrinsic);
     1021                }
     1022                if (dst->result.as<ast::ReferenceType>()) {
     1023                        for (int depth = dst->result->referenceDepth(); depth > 0; depth--) {
     1024                                dst = new ast::AddressExpr(dst);
     1025                        }
     1026                }
     1027                else {
     1028                        dst = new ast::CastExpr(dst, new ast::ReferenceType(dst->result, {}));
     1029                }
     1030                if (src->result.as<ast::ReferenceType>()) {
     1031                        for (int depth = src->result->referenceDepth(); depth > 0; depth--) {
     1032                                src = new ast::AddressExpr(src);
     1033                        }
     1034                }
     1035                return new ast::ApplicationExpr(dst->location, ast::VariableExpr::functionPointer(dst->location, assign), {dst, src});
     1036        }
     1037
    9411038        struct ConstExprChecker : public WithShortCircuiting {
    9421039                // most expressions are not const expr
     
    9791076        };
    9801077
     1078        struct ConstExprChecker_new : public ast::WithShortCircuiting {
     1079                // most expressions are not const expr
     1080                void previsit( const ast::Expr * ) { result = false; visit_children = false; }
     1081
     1082                void previsit( const ast::AddressExpr *addressExpr ) {
     1083                        visit_children = false;
     1084                        const ast::Expr * arg = addressExpr->arg;
     1085
     1086                        // address of a variable or member expression is constexpr
     1087                        if ( ! dynamic_cast< const ast::NameExpr * >( arg )
     1088                        && ! dynamic_cast< const ast::VariableExpr * >( arg )
     1089                        && ! dynamic_cast< const ast::MemberExpr * >( arg )
     1090                        && ! dynamic_cast< const ast::UntypedMemberExpr * >( arg ) ) result = false;
     1091                }
     1092
     1093                // these expressions may be const expr, depending on their children
     1094                void previsit( const ast::SizeofExpr * ) {}
     1095                void previsit( const ast::AlignofExpr * ) {}
     1096                void previsit( const ast::UntypedOffsetofExpr * ) {}
     1097                void previsit( const ast::OffsetofExpr * ) {}
     1098                void previsit( const ast::OffsetPackExpr * ) {}
     1099                void previsit( const ast::CommaExpr * ) {}
     1100                void previsit( const ast::LogicalExpr * ) {}
     1101                void previsit( const ast::ConditionalExpr * ) {}
     1102                void previsit( const ast::CastExpr * ) {}
     1103                void previsit( const ast::ConstantExpr * ) {}
     1104
     1105                void previsit( const ast::VariableExpr * varExpr ) {
     1106                        visit_children = false;
     1107
     1108                        if ( auto inst = varExpr->result.as<ast::EnumInstType>() ) {
     1109                                long long int value;
     1110                                if ( inst->base->valueOf( varExpr->var, value ) ) {
     1111                                        // enumerators are const expr
     1112                                        return;
     1113                                }
     1114                        }
     1115                        result = false;
     1116                }
     1117
     1118                bool result = true;
     1119        };
     1120
    9811121        bool isConstExpr( Expression * expr ) {
    9821122                if ( expr ) {
     
    9981138        }
    9991139
     1140        bool isConstExpr( const ast::Expr * expr ) {
     1141                if ( expr ) {
     1142                        ast::Pass<ConstExprChecker_new> checker;
     1143                        expr->accept( checker );
     1144                        return checker.core.result;
     1145                }
     1146                return true;
     1147        }
     1148
     1149        bool isConstExpr( const ast::Init * init ) {
     1150                if ( init ) {
     1151                        ast::Pass<ConstExprChecker_new> checker;
     1152                        init->accept( checker );
     1153                        return checker.core.result;
     1154                } // if
     1155                // for all intents and purposes, no initializer means const expr
     1156                return true;
     1157        }
     1158
    10001159        bool isConstructor( const std::string & str ) { return str == "?{}"; }
    10011160        bool isDestructor( const std::string & str ) { return str == "^?{}"; }
     
    10261185                if ( ftype->params.size() != 2 ) return false;
    10271186
    1028                 const ast::Type * t1 = getPointerBase( ftype->params.front()->get_type() );
     1187                const ast::Type * t1 = getPointerBase( ftype->params.front() );
    10291188                if ( ! t1 ) return false;
    1030                 const ast::Type * t2 = ftype->params.back()->get_type();
     1189                const ast::Type * t2 = ftype->params.back();
    10311190
    10321191                return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, ast::SymbolTable{} );
     
    10551214                return isCopyFunction( decl, "?{}" );
    10561215        }
     1216
     1217        void addDataSectonAttribute( ObjectDecl * objDecl ) {
     1218                Type *strLitT = new PointerType( Type::Qualifiers( ),
     1219                        new BasicType( Type::Qualifiers( ), BasicType::Char ) );
     1220                std::list< Expression * > attr_params;
     1221                attr_params.push_back(
     1222                        new ConstantExpr( Constant( strLitT, "\".data#\"", std::nullopt ) ) );
     1223                objDecl->attributes.push_back(new Attribute("section", attr_params));
     1224        }
     1225
     1226        void addDataSectionAttribute( ast::ObjectDecl * objDecl ) {
     1227                auto strLitT = new ast::PointerType(new ast::BasicType(ast::BasicType::Char));
     1228                objDecl->attributes.push_back(new ast::Attribute("section", {new ast::ConstantExpr(objDecl->location, strLitT, "\".data#\"", std::nullopt)}));
     1229        }
     1230
    10571231}
  • src/InitTweak/InitTweak.h

    r3c64c668 r58fe85a  
    3838        /// returns the first parameter of a constructor/destructor/assignment function
    3939        ObjectDecl * getParamThis( FunctionType * ftype );
     40        const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func);
    4041
    4142        /// generate a bitwise assignment operation.
    4243        ApplicationExpr * createBitwiseAssignment( Expression * dst, Expression * src );
     44
     45        ast::Expr * createBitwiseAssignment( const ast::Expr * dst, const ast::Expr * src);
    4346
    4447        /// transform Initializer into an argument list that can be passed to a call expression
     
    4851        /// True if the resolver should try to construct dwt
    4952        bool tryConstruct( DeclarationWithType * dwt );
     53        bool tryConstruct( const ast::DeclWithType * dwt );
    5054
    5155        /// True if the type can have a user-defined constructor
    5256        bool isConstructable( Type * t );
     57        bool isConstructable( const ast::Type * t );
    5358
    5459        /// True if the Initializer contains designations
    5560        bool isDesignated( Initializer * init );
     61        bool isDesignated( const ast::Init * init );
    5662
    5763        /// True if the ObjectDecl's Initializer nesting level is not deeper than the depth of its
    5864        /// type, where the depth of its type is the number of nested ArrayTypes + 1
    5965        bool checkInitDepth( ObjectDecl * objDecl );
     66        bool checkInitDepth( const ast::ObjectDecl * objDecl );
    6067
    6168        /// returns the declaration of the function called by the expr (must be ApplicationExpr or UntypedExpr)
     
    7986        /// get all Ctor/Dtor call expressions from a Statement
    8087        void collectCtorDtorCalls( Statement * stmt, std::list< Expression * > & matches );
    81         std::vector< ast::ptr< ast::Expr > > collectCtorDtorCalls( const ast::Stmt * stmt );
     88        std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt );
    8289
    8390        /// get the Ctor/Dtor call expression from a Statement that looks like a generated ctor/dtor call
     
    102109        bool isConstExpr( Expression * expr );
    103110        bool isConstExpr( Initializer * init );
     111
     112        bool isConstExpr( const ast::Expr * expr );
     113        bool isConstExpr( const ast::Init * init );
     114
     115        /// Modifies objDecl to have:
     116        ///    __attribute__((section (".data#")))
     117        /// which makes gcc put the declared variable in the data section,
     118        /// which is helpful for global constants on newer gcc versions,
     119        /// so that CFA's generated initialization won't segfault when writing it via a const cast.
     120        /// The trailing # is an injected assembly comment, to suppress the "a" in
     121        ///    .section .data,"a"
     122        ///    .section .data#,"a"
     123        /// to avoid assembler warning "ignoring changed section attributes for .data"
     124        void addDataSectonAttribute( ObjectDecl * objDecl );
     125
     126        void addDataSectionAttribute( ast::ObjectDecl * objDecl );
    104127
    105128        class InitExpander_old {
  • src/InitTweak/module.mk

    r3c64c668 r58fe85a  
    1515###############################################################################
    1616
    17 SRC += InitTweak/GenInit.cc \
     17SRC += \
     18        InitTweak/FixGlobalInit.cc \
     19        InitTweak/FixGlobalInit.h \
    1820        InitTweak/FixInit.cc \
    19         InitTweak/FixGlobalInit.cc \
    20         InitTweak/InitTweak.cc
     21        InitTweak/FixInit.h \
     22        InitTweak/GenInit.cc \
     23        InitTweak/GenInit.h \
     24        InitTweak/InitTweak.cc \
     25        InitTweak/InitTweak.h \
     26        InitTweak/FixInitNew.cpp
    2127
    22 SRCDEMANGLE += InitTweak/GenInit.cc \
    23         InitTweak/InitTweak.cc
     28SRCDEMANGLE += \
     29        InitTweak/GenInit.cc \
     30        InitTweak/GenInit.h \
     31        InitTweak/InitTweak.cc \
     32        InitTweak/InitTweak.h
    2433
  • src/Makefile.am

    r3c64c668 r58fe85a  
    2020
    2121SRC = main.cc \
     22      CompilationState.cc \
     23      CompilationState.h \
    2224      MakeLibCfa.cc \
    23       CompilationState.cc
     25        MakeLibCfa.h
    2426
    2527SRCDEMANGLE = CompilationState.cc
     
    6668___driver_cfa_cpp_SOURCES = $(SRC)
    6769___driver_cfa_cpp_LDADD = -ldl $(LIBPROFILER) $(LIBTCMALLOC)
     70EXTRA_DIST = include/cassert include/optional BasicTypes-gen.cc
    6871
    6972AM_CXXFLAGS = @HOST_FLAGS@ -Wno-deprecated -Wall -Wextra -DDEBUG_ALL -I./Parser -I$(srcdir)/Parser -I$(srcdir)/include -DYY_NO_INPUT -O3 -g -std=c++14 $(TCMALLOCFLAG)
  • src/Parser/DeclarationNode.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Sat May 16 12:34:05 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Dec 16 15:32:22 2019
    13 // Update Count     : 1133
     12// Last Modified On : Thu Oct  8 08:03:38 2020
     13// Update Count     : 1135
    1414//
    1515
     
    10161016                        if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( decl ) ) {
    10171017                                dwt->location = cur->location;
    1018                                 * out++ = dwt;
     1018                                *out++ = dwt;
    10191019                        } else if ( StructDecl * agg = dynamic_cast< StructDecl * >( decl ) ) {
    10201020                                // e.g., int foo(struct S) {}
     
    10221022                                auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr );
    10231023                                obj->location = cur->location;
    1024                                 * out++ = obj;
     1024                                *out++ = obj;
    10251025                                delete agg;
    10261026                        } else if ( UnionDecl * agg = dynamic_cast< UnionDecl * >( decl ) ) {
     
    10291029                                auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr );
    10301030                                obj->location = cur->location;
    1031                                 * out++ = obj;
     1031                                *out++ = obj;
    10321032                        } else if ( EnumDecl * agg = dynamic_cast< EnumDecl * >( decl ) ) {
    10331033                                // e.g., int foo(enum E) {}
     
    10351035                                auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr );
    10361036                                obj->location = cur->location;
    1037                                 * out++ = obj;
     1037                                *out++ = obj;
    10381038                        } // if
    10391039                } catch( SemanticErrorException & e ) {
     
    11151115        // SUE's cannot have function specifiers, either
    11161116        //
    1117         //    inlne _Noreturn struct S { ... };         // disallowed
    1118         //    inlne _Noreturn enum   E { ... };         // disallowed
     1117        //    inline _Noreturn struct S { ... };                // disallowed
     1118        //    inline _Noreturn enum   E { ... };                // disallowed
    11191119        if ( funcSpecs.any() ) {
    11201120                SemanticError( this, "invalid function specifier for " );
  • src/Parser/ExpressionNode.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Sat May 16 13:17:07 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Dec 18 21:14:58 2019
    13 // Update Count     : 981
     12// Last Modified On : Thu Aug 20 14:01:46 2020
     13// Update Count     : 1076
    1414//
    1515
     
    6565
    6666void lnthSuffix( string & str, int & type, int & ltype ) {
     67        // 'u' can appear before or after length suffix
    6768        string::size_type posn = str.find_last_of( "lL" );
    6869
    6970        if ( posn == string::npos ) return;                                     // no suffix
    70         if ( posn == str.length() - 1 ) { type = 3; return; } // no length => long
    71 
     71        size_t end = str.length() - 1;
     72        if ( posn == end ) { type = 3; return; }                        // no length after 'l' => long
     73       
    7274        string::size_type next = posn + 1;                                      // advance to length
    7375        if ( str[next] == '3' ) {                                                       // 32
     
    8486                } // if
    8587        } // if
    86         // remove "lL" for these cases because it may not imply long
    87         str.erase( posn );                                                                      // remove length
     88
     89        char fix = '\0';
     90        if ( str[end] == 'u' || str[end] == 'U' ) fix = str[end]; // ends with 'uU' ?
     91        str.erase( posn );                                                                      // remove length suffix and possibly uU
     92        if ( type == 5 ) {                                                                      // L128 does not need uU
     93                end = str.length() - 1;
     94                if ( str[end] == 'u' || str[end] == 'U' ) str.erase( end ); // ends with 'uU' ? remove
     95        } else if ( fix != '\0' ) str += fix;                           // put 'uU' back if removed
    8896} // lnthSuffix
    8997
     
    108116} // valueToType
    109117
     118static void scanbin( string & str, unsigned long long int & v ) {
     119        v = 0;
     120        size_t last = str.length() - 1;                                         // last subscript of constant
     121        for ( unsigned int i = 2;; ) {                                          // ignore prefix
     122                if ( str[i] == '1' ) v |= 1;
     123                i += 1;
     124          if ( i == last - 1 || (str[i] != '0' && str[i] != '1') ) break;
     125                v <<= 1;
     126        } // for
     127} // scanbin
     128
    110129Expression * build_constantInteger( string & str ) {
    111130        static const BasicType::Kind kind[2][6] = {
    112131                // short (h) must be before char (hh) because shorter type has the longer suffix
    113                 { BasicType::ShortSignedInt, BasicType::SignedChar, BasicType::SignedInt, BasicType::LongSignedInt, BasicType::LongLongSignedInt, BasicType::SignedInt128, },
    114                 { BasicType::ShortUnsignedInt, BasicType::UnsignedChar, BasicType::UnsignedInt, BasicType::LongUnsignedInt, BasicType::LongLongUnsignedInt, BasicType::UnsignedInt128, },
     132                { BasicType::ShortSignedInt, BasicType::SignedChar, BasicType::SignedInt, BasicType::LongSignedInt, BasicType::LongLongSignedInt, /* BasicType::SignedInt128 */ BasicType::LongLongSignedInt, },
     133                { BasicType::ShortUnsignedInt, BasicType::UnsignedChar, BasicType::UnsignedInt, BasicType::LongUnsignedInt, BasicType::LongLongUnsignedInt, /* BasicType::UnsignedInt128 */ BasicType::LongLongUnsignedInt, },
    115134        };
    116135
     
    120139        }; // lnthsInt
    121140
    122         unsigned long long int v;                                                       // converted integral value
    123         size_t last = str.length() - 1;                                         // last subscript of constant
    124         Expression * ret;
    125         //string fred( str );
     141        string str2( "0x0" );
     142        unsigned long long int v, v2 = 0;                                       // converted integral value
     143        Expression * ret, * ret2;
    126144
    127145        int type = -1;                                                                          // 0 => short, 1 => char, 2 => int, 3 => long int, 4 => long long int, 5 => int128
     
    139157        } // if
    140158
     159        string::size_type posn;
     160
     161        // 'u' can appear before or after length suffix
     162        if ( str.find_last_of( "uU" ) != string::npos ) Unsigned = true;
     163
     164        if ( isdigit( str[str.length() - 1] ) ) {                       // no suffix ?
     165                lnthSuffix( str, type, ltype );                                 // could have length suffix
     166        } else {
     167                // At least one digit in integer constant, so safe to backup while looking for suffix.
     168
     169                posn = str.find_last_of( "pP" );                                // pointer value
     170                if ( posn != string::npos ) { ltype = 5; str.erase( posn, 1 ); goto FINI; }
     171
     172                posn = str.find_last_of( "zZ" );                                // size_t
     173                if ( posn != string::npos ) { Unsigned = true; type = 2; ltype = 4; str.erase( posn, 1 ); goto FINI; }
     174
     175                posn = str.rfind( "hh" );                                               // char
     176                if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; }
     177
     178                posn = str.rfind( "HH" );                                               // char
     179                if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; }
     180
     181                posn = str.find_last_of( "hH" );                                // short
     182                if ( posn != string::npos ) { type = 0; str.erase( posn, 1 ); goto FINI; }
     183
     184                posn = str.find_last_of( "nN" );                                // int (natural number)
     185                if ( posn != string::npos ) { type = 2; str.erase( posn, 1 ); goto FINI; }
     186
     187                if ( str.rfind( "ll" ) != string::npos || str.rfind( "LL" ) != string::npos ) { type = 4; goto FINI; }
     188
     189                lnthSuffix( str, type, ltype );                                 // must be after check for "ll"
     190          FINI: ;
     191        } // if
     192
    141193        // Cannot be just "0"/"1"; sscanf stops at the suffix, if any; value goes over the wall => always generate
    142194
     195#if ! defined(__SIZEOF_INT128__)
     196        if ( type == 5 ) SemanticError( yylloc, "int128 constant is not supported on this target " + str );
     197#endif // ! __SIZEOF_INT128__
     198       
    143199        if ( str[0] == '0' ) {                                                          // radix character ?
    144200                dec = false;
    145201                if ( checkX( str[1] ) ) {                                               // hex constant ?
    146                         sscanf( (char *)str.c_str(), "%llx", &v );
     202                        if ( type < 5 ) {                                                       // not L128 ?
     203                                sscanf( (char *)str.c_str(), "%llx", &v );
     204#if defined(__SIZEOF_INT128__)
     205                        } else {                                                                        // hex int128 constant
     206                                unsigned int len = str.length();
     207                                if ( len > (2 + 16 + 16) ) SemanticError( yylloc, "128-bit hexadecimal constant to large " + str );
     208                          if ( len <= (2 + 16) ) goto FHEX1;            // hex digits < 2^64
     209                                str2 = "0x" + str.substr( len - 16 );
     210                                sscanf( (char *)str2.c_str(), "%llx", &v2 );
     211                                str = str.substr( 0, len - 16 );
     212                          FHEX1: ;
     213                                sscanf( (char *)str.c_str(), "%llx", &v );
     214#endif // __SIZEOF_INT128__
     215                        } // if
    147216                        //printf( "%llx %llu\n", v, v );
    148217                } else if ( checkB( str[1] ) ) {                                // binary constant ?
    149                         v = 0;                                                                          // compute value
    150                         for ( unsigned int i = 2;; ) {                          // ignore prefix
    151                                 if ( str[i] == '1' ) v |= 1;
    152                                 i += 1;
    153                           if ( i == last - 1 || (str[i] != '0' && str[i] != '1') ) break;
    154                                 v <<= 1;
    155                         } // for
     218#if defined(__SIZEOF_INT128__)
     219                        unsigned int len = str.length();
     220                        if ( type == 5 && len > 2 + 64 ) {
     221                                if ( len > 2 + 64 + 64 ) SemanticError( yylloc, "128-bit binary constant to large " + str );
     222                                str2 = "0b" + str.substr( len - 64 );
     223                                str = str.substr( 0, len - 64 );
     224                                scanbin( str2, v2 );
     225                        } // if
     226#endif // __SIZEOF_INT128__
     227                        scanbin( str, v );
    156228                        //printf( "%#llx %llu\n", v, v );
    157229                } else {                                                                                // octal constant
    158                         sscanf( (char *)str.c_str(), "%llo", &v );
     230                        if ( type < 5 ) {                                                       // not L128 ?
     231                                sscanf( (char *)str.c_str(), "%llo", &v );
     232#if defined(__SIZEOF_INT128__)
     233                        } else {                                                                        // octal int128 constant
     234                                unsigned int len = str.length();
     235                                if ( len > 1 + 43 || (len == 1 + 43 && str[0] > '3') ) SemanticError( yylloc, "128-bit octal constant to large " + str );
     236                                char buf[32];
     237                                if ( len <= 1 + 21 ) {                                  // value < 21 octal digitis
     238                                        sscanf( (char *)str.c_str(), "%llo", &v );
     239                                } else {
     240                                        sscanf( &str[len - 21], "%llo", &v );
     241                                        __int128 val = v;                                       // accumulate bits
     242                                        str[len - 21] ='\0';                            // shorten string
     243                                        sscanf( &str[len == 43 ? 1 : 0], "%llo", &v );
     244                                        val |= (__int128)v << 63;                       // store bits
     245                                        if ( len == 1 + 43 ) {                          // most significant 2 bits ?
     246                                                str[2] = '\0';                                  // shorten string
     247                                                sscanf( &str[1], "%llo", &v );  // process most significant 2 bits
     248                                                val |= (__int128)v << 126;              // store bits
     249                                        } // if
     250                                        v = val >> 64; v2 = (uint64_t)val;      // replace octal constant with 2 hex constants
     251                                        sprintf( buf, "%#llx", v2 );
     252                                        str2 = buf;
     253                                } // if
     254                                sprintf( buf, "%#llx", v );
     255                                str = buf;
     256#endif // __SIZEOF_INT128__
     257                        } // if
    159258                        //printf( "%#llo %llu\n", v, v );
    160259                } // if
    161260        } else {                                                                                        // decimal constant ?
    162                 sscanf( (char *)str.c_str(), "%llu", &v );
     261                if ( type < 5 ) {                                                               // not L128 ?
     262                        sscanf( (char *)str.c_str(), "%llu", &v );
     263#if defined(__SIZEOF_INT128__)
     264                } else {                                                                                // decimal int128 constant
     265                        #define P10_UINT64 10'000'000'000'000'000'000ULL // 19 zeroes
     266                        unsigned int len = str.length();
     267                        if ( str.length() == 39 && str > (Unsigned ? "340282366920938463463374607431768211455" : "170141183460469231731687303715884105727") )
     268                                SemanticError( yylloc, "128-bit decimal constant to large " + str );
     269                        char buf[32];
     270                        if ( len <= 19 ) {                                                      // value < 19 decimal digitis
     271                                sscanf( (char *)str.c_str(), "%llu", &v );
     272                        } else {
     273                                sscanf( &str[len - 19], "%llu", &v );
     274                                __int128 val = v;                                               // accumulate bits
     275                                str[len - 19] ='\0';                                    // shorten string
     276                                sscanf( &str[len == 39 ? 1 : 0], "%llu", &v );
     277                                val += (__int128)v * (__int128)P10_UINT64; // store bits
     278                                if ( len == 39 ) {                                              // most significant 2 bits ?
     279                                        str[1] = '\0';                                          // shorten string
     280                                        sscanf( &str[0], "%llu", &v );          // process most significant 2 bits
     281                                        val += (__int128)v * (__int128)P10_UINT64 * (__int128)P10_UINT64; // store bits
     282                                } // if
     283                                v = val >> 64; v2 = (uint64_t)val;              // replace decimal constant with 2 hex constants
     284                                sprintf( buf, "%#llx", v2 );
     285                                str2 = buf;
     286                        } // if
     287                        sprintf( buf, "%#llx", v );
     288                        str = buf;
     289#endif // __SIZEOF_INT128__
     290                } // if
    163291                //printf( "%llu\n", v );
    164292        } // if
    165293
    166         string::size_type posn;
    167 
    168         if ( isdigit( str[last] ) ) {                                           // no suffix ?
    169                 lnthSuffix( str, type, ltype );                                 // could have length suffix
    170                 if ( type == -1 ) {                                                             // no suffix
    171                         valueToType( v, dec, type, Unsigned );
    172                 } // if
    173         } else {
    174                 // At least one digit in integer constant, so safe to backup while looking for suffix.
    175 
    176                 posn = str.find_last_of( "pP" );
    177                 if ( posn != string::npos ) { valueToType( v, dec, type, Unsigned ); ltype = 5; str.erase( posn, 1 ); goto FINI; }
    178 
    179                 posn = str.find_last_of( "zZ" );
    180                 if ( posn != string::npos ) { Unsigned = true; type = 2; ltype = 4; str.erase( posn, 1 ); goto FINI; }
    181 
    182                 // 'u' can appear before or after length suffix
    183                 if ( str.find_last_of( "uU" ) != string::npos ) Unsigned = true;
    184 
    185                 posn = str.rfind( "hh" );
    186                 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; }
    187 
    188                 posn = str.rfind( "HH" );
    189                 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; }
    190 
    191                 posn = str.find_last_of( "hH" );
    192                 if ( posn != string::npos ) { type = 0; str.erase( posn, 1 ); goto FINI; }
    193 
    194                 posn = str.find_last_of( "nN" );
    195                 if ( posn != string::npos ) { type = 2; str.erase( posn, 1 ); goto FINI; }
    196 
    197                 if ( str.rfind( "ll" ) != string::npos || str.rfind( "LL" ) != string::npos ) { type = 4; goto FINI; }
    198 
    199                 lnthSuffix( str, type, ltype );                                 // must be after check for "ll"
    200                 if ( type == -1 ) {                                                             // only 'u' suffix ?
    201                         valueToType( v, dec, type, Unsigned );
    202                 } // if
    203           FINI: ;
    204         } // if
     294        if ( type == -1 ) {                                                                     // no suffix => determine type from value size
     295                valueToType( v, dec, type, Unsigned );
     296        } // if
     297        /* printf( "%s %llo %s %llo\n", str.c_str(), v, str2.c_str(), v2 ); */
    205298
    206299        //if ( !( 0 <= type && type <= 6 ) ) { printf( "%s %lu %d %s\n", fred.c_str(), fred.length(), type, str.c_str() ); }
     
    214307        } else if ( ltype != -1 ) {                                                     // explicit length ?
    215308                if ( ltype == 6 ) {                                                             // int128, (int128)constant
    216                         ret = new CastExpr( ret, new BasicType( Type::Qualifiers(), kind[Unsigned][type] ), false );
     309//                      ret = new CastExpr( ret, new BasicType( Type::Qualifiers(), kind[Unsigned][type] ), false );
     310                        ret2 = new ConstantExpr( Constant( new BasicType( noQualifiers, BasicType::LongLongSignedInt ), str2, v2 ) );
     311                        ret = build_compoundLiteral( DeclarationNode::newBasicType( DeclarationNode::Int128 )->addType( DeclarationNode::newSignedNess( DeclarationNode::Unsigned ) ),
     312                                                                                 new InitializerNode( (InitializerNode *)(new InitializerNode( new ExpressionNode( v2 == 0 ? ret2 : ret ) ))->set_last( new InitializerNode( new ExpressionNode( v2 == 0 ? ret : ret2 ) ) ), true ) );
    217313                } else {                                                                                // explicit length, (length_type)constant
    218314                        ret = new CastExpr( ret, new TypeInstType( Type::Qualifiers(), lnthsInt[Unsigned][ltype], false ), false );
     
    342438                if ( str[1] == '8' ) goto Default;                              // utf-8 characters => array of char
    343439                // lookup type of associated typedef
    344                 strtype = new TypeInstType( Type::Qualifiers( Type::Const ), "char16_t", false );
     440                strtype = new TypeInstType( Type::Qualifiers( ), "char16_t", false );
    345441                break;
    346442          case 'U':
    347                 strtype = new TypeInstType( Type::Qualifiers( Type::Const ), "char32_t", false );
     443                strtype = new TypeInstType( Type::Qualifiers( ), "char32_t", false );
    348444                break;
    349445          case 'L':
    350                 strtype = new TypeInstType( Type::Qualifiers( Type::Const ), "wchar_t", false );
     446                strtype = new TypeInstType( Type::Qualifiers( ), "wchar_t", false );
    351447                break;
    352448          Default:                                                                                      // char default string type
    353449          default:
    354                 strtype = new BasicType( Type::Qualifiers( Type::Const ), BasicType::Char );
     450                strtype = new BasicType( Type::Qualifiers( ), BasicType::Char );
    355451        } // switch
    356452        ArrayType * at = new ArrayType( noQualifiers, strtype,
  • src/Parser/ParseNode.h

    r3c64c668 r58fe85a  
    1010// Created On       : Sat May 16 13:28:16 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Feb  7 17:56:02 2020
    13 // Update Count     : 891
     12// Last Modified On : Sat Oct 24 03:53:54 2020
     13// Update Count     : 895
    1414//
    1515
     
    3737class Attribute;
    3838class Declaration;
    39 class DeclarationNode;
     39struct DeclarationNode;
    4040class DeclarationWithType;
    4141class ExpressionNode;
    4242class Initializer;
    43 class StatementNode;
     43struct StatementNode;
    4444
    4545//##############################################################################
     
    8686class InitializerNode : public ParseNode {
    8787  public:
    88         InitializerNode( ExpressionNode *, bool aggrp = false,  ExpressionNode * des = nullptr );
     88        InitializerNode( ExpressionNode *, bool aggrp = false, ExpressionNode * des = nullptr );
    8989        InitializerNode( InitializerNode *, bool aggrp = false, ExpressionNode * des = nullptr );
    9090        InitializerNode( bool isDelete );
     
    205205struct TypeData;
    206206
    207 class DeclarationNode : public ParseNode {
    208   public:
     207struct DeclarationNode : public ParseNode {
    209208        // These enumerations must harmonize with their names in DeclarationNode.cc.
    210209        enum BasicType { Void, Bool, Char, Int, Int128,
     
    304303        bool get_inLine() const { return inLine; }
    305304        DeclarationNode * set_inLine( bool inL ) { inLine = inL; return this; }
    306   public:
     305
    307306        DeclarationNode * get_last() { return (DeclarationNode *)ParseNode::get_last(); }
    308307
     
    360359//##############################################################################
    361360
    362 class StatementNode final : public ParseNode {
    363   public:
     361struct StatementNode final : public ParseNode {
    364362        StatementNode() { stmt = nullptr; }
    365363        StatementNode( Statement * stmt ) : stmt( stmt ) {}
     
    382380                os << stmt.get() << std::endl;
    383381        }
    384   private:
     382
    385383        std::unique_ptr<Statement> stmt;
    386384}; // StatementNode
     
    426424Statement * build_finally( StatementNode * stmt );
    427425Statement * build_compound( StatementNode * first );
     426StatementNode * maybe_build_compound( StatementNode * first );
    428427Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output = nullptr, ExpressionNode * input = nullptr, ExpressionNode * clobber = nullptr, LabelNode * gotolabels = nullptr );
    429428Statement * build_directive( std::string * directive );
     429SuspendStmt * build_suspend( StatementNode *, SuspendStmt::Type = SuspendStmt::None);
    430430WaitForStmt * build_waitfor( ExpressionNode * target, StatementNode * stmt, ExpressionNode * when );
    431431WaitForStmt * build_waitfor( ExpressionNode * target, StatementNode * stmt, ExpressionNode * when, WaitForStmt * existing );
  • src/Parser/StatementNode.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Sat May 16 14:59:41 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Aug  4 09:39:25 2018
    13 // Update Count     : 363
     12// Last Modified On : Sat Oct 24 04:20:55 2020
     13// Update Count     : 383
    1414//
    1515
     
    249249} // build_finally
    250250
     251SuspendStmt * build_suspend( StatementNode * then, SuspendStmt::Type type ) {
     252        auto node = new SuspendStmt();
     253
     254        node->type = type;
     255
     256        std::list< Statement * > stmts;
     257        buildMoveList< Statement, StatementNode >( then, stmts );
     258        if(!stmts.empty()) {
     259                assert( stmts.size() == 1 );
     260                node->then = dynamic_cast< CompoundStmt * >( stmts.front() );
     261        }
     262
     263        return node;
     264}
     265
    251266WaitForStmt * build_waitfor( ExpressionNode * targetExpr, StatementNode * stmt, ExpressionNode * when ) {
    252267        auto node = new WaitForStmt();
     
    330345} // build_compound
    331346
     347// A single statement in a control structure is always converted to a compound statement so subsequent generated code
     348// can be placed within this compound statement. Otherwise, code generation has to constantly check for a single
     349// statement and wrap it into a compound statement to insert additional code. Hence, all control structures have a
     350// conical form for code generation.
     351StatementNode * maybe_build_compound( StatementNode * first ) {
     352        // Optimization: if the control-structure statement is a compound statement, do not wrap it.
     353        // e.g., if (...) {...} do not wrap the existing compound statement.
     354        if ( ! dynamic_cast<CompoundStmt *>( first->stmt.get() ) ) { // unique_ptr
     355                CompoundStmt * cs = new CompoundStmt();
     356                buildMoveList( first, cs->get_kids() );
     357                return new StatementNode( cs );
     358        } // if
     359        return first;
     360} // maybe_build_compound
     361
    332362Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) {
    333363        std::list< Expression * > out, in;
  • src/Parser/TypeData.cc

    r3c64c668 r58fe85a  
    769769          case AggregateDecl::Struct:
    770770          case AggregateDecl::Coroutine:
     771          case AggregateDecl::Generator:
    771772          case AggregateDecl::Monitor:
    772773          case AggregateDecl::Thread:
     
    899900                ret = new TypeDecl( name, scs, typebuild( td->base ), TypeDecl::Dtype, true );
    900901        } // if
    901         buildList( td->symbolic.params, ret->get_parameters() );
    902902        buildList( td->symbolic.assertions, ret->get_assertions() );
    903903        ret->base->attributes.splice( ret->base->attributes.end(), attributes );
  • src/Parser/lex.ll

    r3c64c668 r58fe85a  
    1010 * Created On       : Sat Sep 22 08:58:10 2001
    1111 * Last Modified By : Peter A. Buhr
    12  * Last Modified On : Sat Feb 15 11:05:50 2020
    13  * Update Count     : 737
     12 * Last Modified On : Tue Oct  6 18:15:41 2020
     13 * Update Count     : 743
    1414 */
    1515
     
    6262#define IDENTIFIER_RETURN()     RETURN_VAL( typedefTable.isKind( yytext ) )
    6363
    64 #ifdef HAVE_KEYWORDS_FLOATXX                                                            // GCC >= 7 => keyword, otherwise typedef
     64#ifdef HAVE_KEYWORDS_FLOATXX                                                    // GCC >= 7 => keyword, otherwise typedef
    6565#define FLOATXX(v) KEYWORD_RETURN(v);
    6666#else
    67 #define FLOATXX(v) IDENTIFIER_RETURN(); 
     67#define FLOATXX(v) IDENTIFIER_RETURN();
    6868#endif // HAVE_KEYWORDS_FLOATXX
    6969
     
    292292__restrict__    { KEYWORD_RETURN(RESTRICT); }                   // GCC
    293293return                  { KEYWORD_RETURN(RETURN); }
    294         /* resume                       { KEYWORD_RETURN(RESUME); }                             // CFA */
     294 /* resume                      { KEYWORD_RETURN(RESUME); }                             // CFA */
    295295short                   { KEYWORD_RETURN(SHORT); }
    296296signed                  { KEYWORD_RETURN(SIGNED); }
     
    301301_Static_assert  { KEYWORD_RETURN(STATICASSERT); }               // C11
    302302struct                  { KEYWORD_RETURN(STRUCT); }
    303         /* suspend                      { KEYWORD_RETURN(SUSPEND); }                    // CFA */
     303suspend                 { KEYWORD_RETURN(SUSPEND); }                    // CFA
    304304switch                  { KEYWORD_RETURN(SWITCH); }
    305305thread                  { KEYWORD_RETURN(THREAD); }                             // C11
  • src/Parser/module.mk

    r3c64c668 r58fe85a  
    1717BUILT_SOURCES = Parser/parser.hh
    1818
    19 AM_YFLAGS = -d -t -v
     19AM_YFLAGS = -d -t -v -Wno-yacc
    2020
    2121SRC += \
     
    2323       Parser/ExpressionNode.cc \
    2424       Parser/InitializerNode.cc \
     25       Parser/lex.ll \
    2526       Parser/ParseNode.cc \
     27       Parser/ParseNode.h \
     28       Parser/parser.yy \
     29       Parser/ParserTypes.h \
     30       Parser/parserutility.cc \
     31       Parser/parserutility.h \
    2632       Parser/StatementNode.cc \
    2733       Parser/TypeData.cc \
     34       Parser/TypeData.h \
    2835       Parser/TypedefTable.cc \
    29        Parser/lex.ll \
    30        Parser/parser.yy \
    31        Parser/parserutility.cc
     36       Parser/TypedefTable.h
    3237
    3338MOSTLYCLEANFILES += Parser/lex.cc Parser/parser.cc Parser/parser.hh Parser/parser.output
  • src/Parser/parser.yy

    r3c64c668 r58fe85a  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Feb 21 14:47:29 2020
    13 // Update Count     : 4468
     12// Last Modified On : Sat Oct 24 08:21:14 2020
     13// Update Count     : 4624
    1414//
    1515
     
    204204                        return forCtrl( type, new string( identifier->name ), start, compop, comp, inc );
    205205                } else {
    206                         SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed" ); return nullptr;
     206                        SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed." ); return nullptr;
    207207                } // if
    208208        } else {
    209                 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed" ); return nullptr;
     209                SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed." ); return nullptr;
    210210        } // if
    211211} // forCtrl
     
    278278%token OTYPE FTYPE DTYPE TTYPE TRAIT                                    // CFA
    279279%token SIZEOF OFFSETOF
    280 // %token SUSPEND RESUME                                                                        // CFA
     280// %token RESUME                                                                                        // CFA
     281%token SUSPEND                                                                                  // CFA
    281282%token ATTRIBUTE EXTENSION                                                              // GCC
    282283%token IF ELSE SWITCH CASE DEFAULT DO WHILE FOR BREAK CONTINUE GOTO RETURN
     
    328329%type<en> conditional_expression                constant_expression                     assignment_expression           assignment_expression_opt
    329330%type<en> comma_expression                              comma_expression_opt
    330 %type<en> argument_expression_list              argument_expression                     default_initialize_opt
     331%type<en> argument_expression_list_opt  argument_expression                     default_initialize_opt
    331332%type<ifctl> if_control_expression
    332333%type<fctl> for_control_expression              for_control_expression_list
     
    369370%type<decl> assertion assertion_list assertion_list_opt
    370371
    371 %type<en>   bit_subrange_size_opt bit_subrange_size
     372%type<en> bit_subrange_size_opt bit_subrange_size
    372373
    373374%type<decl> basic_declaration_specifier basic_type_name basic_type_specifier direct_type indirect_type
     
    623624                // equivalent to the old x[i,j].
    624625                { $$ = new ExpressionNode( build_binary_val( OperKinds::Index, $1, $3 ) ); }
    625         | postfix_expression '{' argument_expression_list '}' // CFA, constructor call
     626        | postfix_expression '{' argument_expression_list_opt '}' // CFA, constructor call
    626627                {
    627628                        Token fn;
     
    629630                        $$ = new ExpressionNode( new ConstructorExpr( build_func( new ExpressionNode( build_varref( fn ) ), (ExpressionNode *)( $1 )->set_last( $3 ) ) ) );
    630631                }
    631         | postfix_expression '(' argument_expression_list ')'
     632        | postfix_expression '(' argument_expression_list_opt ')'
    632633                { $$ = new ExpressionNode( build_func( $1, $3 ) ); }
    633634        | postfix_expression '`' identifier                                     // CFA, postfix call
     
    661662        | '(' type_no_function ')' '@' '{' initializer_list_opt comma_opt '}' // CFA, explicit C compound-literal
    662663                { $$ = new ExpressionNode( build_compoundLiteral( $2, (new InitializerNode( $6, true ))->set_maybeConstructed( false ) ) ); }
    663         | '^' primary_expression '{' argument_expression_list '}' // CFA, destructor call
     664        | '^' primary_expression '{' argument_expression_list_opt '}' // CFA, destructor call
    664665                {
    665666                        Token fn;
     
    669670        ;
    670671
    671 argument_expression_list:
     672argument_expression_list_opt:
    672673        // empty
    673674                { $$ = nullptr; }
    674675        | argument_expression
    675         | argument_expression_list ',' argument_expression
     676        | argument_expression_list_opt ',' argument_expression
    676677                { $$ = (ExpressionNode *)($1->set_last( $3 )); }
    677678        ;
     
    792793        | '(' aggregate_control '&' ')' cast_expression         // CFA
    793794                { $$ = new ExpressionNode( build_keyword_cast( $2, $5 ) ); }
    794                 // VIRTUAL cannot be opt because of look ahead issues
    795795        | '(' VIRTUAL ')' cast_expression                                       // CFA
    796796                { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression >( $4 ), maybeMoveBuildType( nullptr ) ) ); }
     
    918918        conditional_expression
    919919        | unary_expression assignment_operator assignment_expression
    920                 { $$ = new ExpressionNode( build_binary_val( $2, $1, $3 ) ); }
     920                {
     921//                      if ( $2 == OperKinds::AtAssn ) {
     922//                              SemanticError( yylloc, "C @= assignment is currently unimplemented." ); $$ = nullptr;
     923//                      } else {
     924                                $$ = new ExpressionNode( build_binary_val( $2, $1, $3 ) );
     925//                      } // if
     926                }
    921927        | unary_expression '=' '{' initializer_list_opt comma_opt '}'
    922928                { SemanticError( yylloc, "Initializer assignment is currently unimplemented." ); $$ = nullptr; }
     
    959965
    960966tuple_expression_list:
    961         assignment_expression_opt
    962         | tuple_expression_list ',' assignment_expression_opt
     967        assignment_expression
     968        | '@'                                                                                           // CFA
     969                { SemanticError( yylloc, "Eliding tuple element with '@' is currently unimplemented." ); $$ = nullptr; }
     970        | tuple_expression_list ',' assignment_expression
    963971                { $$ = (ExpressionNode *)($1->set_last( $3 )); }
     972        | tuple_expression_list ',' '@'
     973                { SemanticError( yylloc, "Eliding tuple element with '@' is currently unimplemented." ); $$ = nullptr; }
    964974        ;
    965975
     
    10701080        IF '(' if_control_expression ')' statement                      %prec THEN
    10711081                // explicitly deal with the shift/reduce conflict on if/else
    1072                 { $$ = new StatementNode( build_if( $3, $5, nullptr ) ); }
     1082                { $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), nullptr ) ); }
    10731083        | IF '(' if_control_expression ')' statement ELSE statement
    1074                 { $$ = new StatementNode( build_if( $3, $5, $7 ) ); }
     1084                { $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), maybe_build_compound( $7 ) ) ); }
    10751085        ;
    10761086
     
    11201130
    11211131case_clause:                                                                                    // CFA
    1122         case_label_list statement                                       { $$ = $1->append_last_case( new StatementNode( build_compound( $2 ) ) ); }
     1132        case_label_list statement                                       { $$ = $1->append_last_case( maybe_build_compound( $2 ) ); }
    11231133        ;
    11241134
     
    11381148iteration_statement:
    11391149        WHILE '(' push if_control_expression ')' statement pop
    1140                 { $$ = new StatementNode( build_while( $4, $6 ) ); }
     1150                { $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 ) ) ); }
    11411151        | WHILE '(' ')' statement                                                       // CFA => while ( 1 )
    1142                 { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), $4 ) ); }
     1152                { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }
    11431153        | DO statement WHILE '(' comma_expression ')' ';'
    1144                 { $$ = new StatementNode( build_do_while( $5, $2 ) ); }
     1154                { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
    11451155        | DO statement WHILE '(' ')' ';'                                        // CFA => do while( 1 )
    1146                 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), $2 ) ); }
     1156                { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); }
    11471157        | FOR '(' push for_control_expression_list ')' statement pop
    1148                 { $$ = new StatementNode( build_for( $4, $6 ) ); }
     1158                { $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 ) ) ); }
    11491159        | FOR '(' ')' statement                                                         // CFA => for ( ;; )
    1150                 { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), $4 ) ); }
     1160                { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); }
    11511161        ;
    11521162
     
    11851195                { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ),
    11861196                                                OperKinds::LThan, $1->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1187         | '=' comma_expression                                                                  // CFA
     1197        | '=' comma_expression                                                          // CFA
    11881198                { $$ = forCtrl( $2, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ),
    11891199                                                OperKinds::LEThan, $2->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
     
    11921202        | comma_expression inclexcl comma_expression '~' comma_expression // CFA
    11931203                { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), $1->clone(), $2, $3, $5 ); }
     1204        | comma_expression ';'                                                          // CFA
     1205                { $$ = forCtrl( new ExpressionNode( build_constantInteger( *new string( "0u" ) ) ), $1, nullptr, OperKinds::LThan, nullptr, nullptr ); }
    11941206        | comma_expression ';' comma_expression                         // CFA
    11951207                { $$ = forCtrl( $3, $1, new ExpressionNode( build_constantInteger( *new string( "0" ) ) ),
    11961208                                                OperKinds::LThan, $3->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1197         | comma_expression ';' '=' comma_expression                             // CFA
     1209        | comma_expression ';' '=' comma_expression                     // CFA
    11981210                { $$ = forCtrl( $4, $1, new ExpressionNode( build_constantInteger( *new string( "0" ) ) ),
    11991211                                                OperKinds::LEThan, $4->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
     
    12591271        | RETURN '{' initializer_list_opt comma_opt '}' ';'
    12601272                { SemanticError( yylloc, "Initializer return is currently unimplemented." ); $$ = nullptr; }
    1261         // | SUSPEND ';'
    1262         //      { SemanticError( yylloc, "Suspend expression is currently unimplemented." ); $$ = nullptr; }
    1263         // | SUSPEND compound_statement ';'
    1264         //      { SemanticError( yylloc, "Suspend expression is currently unimplemented." ); $$ = nullptr; }
     1273        | SUSPEND ';'
     1274                { $$ = new StatementNode( build_suspend( nullptr ) ); }
     1275        | SUSPEND compound_statement
     1276                { $$ = new StatementNode( build_suspend( $2 ) ); }
     1277        | SUSPEND COROUTINE ';'
     1278                { $$ = new StatementNode( build_suspend( nullptr, SuspendStmt::Coroutine ) ); }
     1279        | SUSPEND COROUTINE compound_statement
     1280                { $$ = new StatementNode( build_suspend( $3, SuspendStmt::Coroutine ) ); }
     1281        | SUSPEND GENERATOR ';'
     1282                { $$ = new StatementNode( build_suspend( nullptr, SuspendStmt::Generator ) ); }
     1283        | SUSPEND GENERATOR compound_statement
     1284                { $$ = new StatementNode( build_suspend( $3, SuspendStmt::Generator ) ); }
    12651285        | THROW assignment_expression_opt ';'                           // handles rethrow
    12661286                { $$ = new StatementNode( build_throw( $2 ) ); }
     
    12851305// If MUTEX becomes a general qualifier, there are shift/reduce conflicts, so change syntax to "with mutex".
    12861306mutex_statement:
    1287         MUTEX '(' argument_expression_list ')' statement
     1307        MUTEX '(' argument_expression_list_opt ')' statement
    12881308                { SemanticError( yylloc, "Mutex statement is currently unimplemented." ); $$ = nullptr; }
    12891309        ;
     
    13021322        WAITFOR '(' cast_expression ')'
    13031323                { $$ = $3; }
    1304 //      | WAITFOR '(' cast_expression ',' argument_expression_list ')'
     1324//      | WAITFOR '(' cast_expression ',' argument_expression_list_opt ')'
    13051325//              { $$ = (ExpressionNode *)$3->set_last( $5 ); }
    1306         | WAITFOR '(' cast_expression_list ':' argument_expression_list ')'
     1326        | WAITFOR '(' cast_expression_list ':' argument_expression_list_opt ')'
    13071327                { $$ = (ExpressionNode *)($3->set_last( $5 )); }
    13081328        ;
     
    13111331        cast_expression
    13121332        | cast_expression_list ',' cast_expression
    1313                 { $$ = (ExpressionNode *)($1->set_last( $3 )); }
     1333                // { $$ = (ExpressionNode *)($1->set_last( $3 )); }
     1334                { SemanticError( yylloc, "List of mutex member is currently unimplemented." ); $$ = nullptr; }
    13141335        ;
    13151336
     
    13201341waitfor_clause:
    13211342        when_clause_opt waitfor statement                                       %prec THEN
    1322                 { $$ = build_waitfor( $2, $3, $1 ); }
     1343                { $$ = build_waitfor( $2, maybe_build_compound( $3 ), $1 ); }
    13231344        | when_clause_opt waitfor statement WOR waitfor_clause
    1324                 { $$ = build_waitfor( $2, $3, $1, $5 ); }
     1345                { $$ = build_waitfor( $2, maybe_build_compound( $3 ), $1, $5 ); }
    13251346        | when_clause_opt timeout statement                                     %prec THEN
    1326                 { $$ = build_waitfor_timeout( $2, $3, $1 ); }
     1347                { $$ = build_waitfor_timeout( $2, maybe_build_compound( $3 ), $1 ); }
    13271348        | when_clause_opt ELSE statement
    1328                 { $$ = build_waitfor_timeout( nullptr, $3, $1 ); }
     1349                { $$ = build_waitfor_timeout( nullptr, maybe_build_compound( $3 ), $1 ); }
    13291350                // "else" must be conditional after timeout or timeout is never triggered (i.e., it is meaningless)
    13301351        | when_clause_opt timeout statement WOR ELSE statement
    13311352                { SemanticError( yylloc, "else clause must be conditional after timeout or timeout never triggered." ); $$ = nullptr; }
    13321353        | when_clause_opt timeout statement WOR when_clause ELSE statement
    1333                 { $$ = build_waitfor_timeout( $2, $3, $1, $7, $5 ); }
     1354                { $$ = build_waitfor_timeout( $2, maybe_build_compound( $3 ), $1, maybe_build_compound( $7 ), $5 ); }
    13341355        ;
    13351356
     
    16541675
    16551676typedef_expression:
    1656                 // GCC, naming expression type: typedef name = exp; gives a name to the type of an expression
     1677                // deprecated GCC, naming expression type: typedef name = exp; gives a name to the type of an expression
    16571678        TYPEDEF identifier '=' assignment_expression
    16581679                {
    1659                         // $$ = DeclarationNode::newName( 0 );                  // unimplemented
    1660                         SemanticError( yylloc, "Typedef expression is currently unimplemented." ); $$ = nullptr;
     1680                        SemanticError( yylloc, "Typedef expression is deprecated, use typeof(...) instead." ); $$ = nullptr;
    16611681                }
    16621682        | typedef_expression pop ',' push identifier '=' assignment_expression
    16631683                {
    1664                         // $$ = DeclarationNode::newName( 0 );                  // unimplemented
    1665                         SemanticError( yylloc, "Typedef expression is currently unimplemented." ); $$ = nullptr;
    1666                 }
    1667         ;
    1668 
    1669 //c_declaration:
    1670 //      declaring_list pop ';'
    1671 //      | typedef_declaration pop ';'
    1672 //      | typedef_expression pop ';'                                            // GCC, naming expression type
    1673 //      | sue_declaration_specifier pop ';'
    1674 //      ;
    1675 //
    1676 //declaring_list:
    1677 //              // A semantic check is required to ensure asm_name only appears on declarations with implicit or explicit static
    1678 //              // storage-class
    1679 //       declarator asm_name_opt initializer_opt
    1680 //              {
    1681 //                      typedefTable.addToEnclosingScope( IDENTIFIER );
    1682 //                      $$ = ( $2->addType( $1 ))->addAsmName( $3 )->addInitializer( $4 );
    1683 //              }
    1684 //      | declaring_list ',' attribute_list_opt declarator asm_name_opt initializer_opt
    1685 //              {
    1686 //                      typedefTable.addToEnclosingScope( IDENTIFIER );
    1687 //                      $$ = $1->appendList( $1->cloneBaseType( $4->addAsmName( $5 )->addInitializer( $6 ) ) );
    1688 //              }
    1689 //      ;
     1684                        SemanticError( yylloc, "Typedef expression is deprecated, use typeof(...) instead." ); $$ = nullptr;
     1685                }
     1686        ;
    16901687
    16911688c_declaration:
     
    16931690                { $$ = distAttr( $1, $2 ); }
    16941691        | typedef_declaration
    1695         | typedef_expression                                                            // GCC, naming expression type
     1692        | typedef_expression                                                            // deprecated GCC, naming expression type
    16961693        | sue_declaration_specifier
    16971694        ;
     
    20722069                { yyy = true; $$ = AggregateDecl::Union; }
    20732070        | EXCEPTION                                                                                     // CFA
    2074                 { yyy = true; $$ = AggregateDecl::Exception; }
     2071                // { yyy = true; $$ = AggregateDecl::Exception; }
     2072                { SemanticError( yylloc, "exception aggregate is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; }
    20752073        ;
    20762074
    20772075aggregate_control:                                                                              // CFA
    2078         GENERATOR
    2079                 { yyy = true; $$ = AggregateDecl::Coroutine; }
     2076        MONITOR
     2077                { yyy = true; $$ = AggregateDecl::Monitor; }
     2078        | MUTEX STRUCT
     2079                { yyy = true; $$ = AggregateDecl::Monitor; }
     2080        | GENERATOR
     2081                { yyy = true; $$ = AggregateDecl::Generator; }
     2082        | MUTEX GENERATOR
     2083                { SemanticError( yylloc, "monitor generator is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; }
    20802084        | COROUTINE
    20812085                { yyy = true; $$ = AggregateDecl::Coroutine; }
    2082         | MONITOR
    2083                 { yyy = true; $$ = AggregateDecl::Monitor; }
     2086        | MUTEX COROUTINE
     2087                { SemanticError( yylloc, "monitor coroutine is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; }
    20842088        | THREAD
    20852089                { yyy = true; $$ = AggregateDecl::Thread; }
     2090        | MUTEX THREAD
     2091                { SemanticError( yylloc, "monitor thread is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; }
    20862092        ;
    20872093
     
    24062412// Overloading: function, data, and operator identifiers may be overloaded.
    24072413//
    2408 // Type declarations: "type" is used to generate new types for declaring objects. Similarly, "dtype" is used for object
     2414// Type declarations: "otype" is used to generate new types for declaring objects. Similarly, "dtype" is used for object
    24092415//     and incomplete types, and "ftype" is used for function types. Type declarations with initializers provide
    24102416//     definitions of new types. Type declarations with storage class "extern" provide opaque types.
     
    24352441        type_class identifier_or_type_name
    24362442                { typedefTable.addToScope( *$2, TYPEDEFname, "9" ); }
    2437           type_initializer_opt assertion_list_opt
     2443        type_initializer_opt assertion_list_opt
    24382444                { $$ = DeclarationNode::newTypeParam( $1, $2 )->addTypeInitializer( $4 )->addAssertions( $5 ); }
    24392445        | type_specifier identifier_parameter_declarator
     
    24622468        assertion
    24632469        | assertion_list assertion
    2464                 { $$ = $1 ? $1->appendList( $2 ) : $2; }
     2470                { $$ = $1->appendList( $2 ); }
    24652471        ;
    24662472
     
    27492755        | attr_name
    27502756                { $$ = DeclarationNode::newAttribute( $1 ); }
    2751         | attr_name '(' argument_expression_list ')'
     2757        | attr_name '(' argument_expression_list_opt ')'
    27522758                { $$ = DeclarationNode::newAttribute( $1, $3 ); }
    27532759        ;
  • src/ResolvExpr/AdjustExprType.cc

    r3c64c668 r58fe85a  
    100100
    101101namespace {
    102         struct AdjustExprType_new final : public ast::WithShortCircuiting {
     102        class AdjustExprType_new final : public ast::WithShortCircuiting {
     103                const ast::SymbolTable & symtab;
     104        public:
    103105                const ast::TypeEnvironment & tenv;
    104                 const ast::SymbolTable & symtab;
    105106
    106107                AdjustExprType_new( const ast::TypeEnvironment & e, const ast::SymbolTable & syms )
    107                 : tenv( e ), symtab( syms ) {}
     108                : symtab( syms ), tenv( e ) {}
    108109
    109                 void premutate( const ast::VoidType * ) { visit_children = false; }
    110                 void premutate( const ast::BasicType * ) { visit_children = false; }
    111                 void premutate( const ast::PointerType * ) { visit_children = false; }
    112                 void premutate( const ast::ArrayType * ) { visit_children = false; }
    113                 void premutate( const ast::FunctionType * ) { visit_children = false; }
    114                 void premutate( const ast::StructInstType * ) { visit_children = false; }
    115                 void premutate( const ast::UnionInstType * ) { visit_children = false; }
    116                 void premutate( const ast::EnumInstType * ) { visit_children = false; }
    117                 void premutate( const ast::TraitInstType * ) { visit_children = false; }
    118                 void premutate( const ast::TypeInstType * ) { visit_children = false; }
    119                 void premutate( const ast::TupleType * ) { visit_children = false; }
    120                 void premutate( const ast::VarArgsType * ) { visit_children = false; }
    121                 void premutate( const ast::ZeroType * ) { visit_children = false; }
    122                 void premutate( const ast::OneType * ) { visit_children = false; }
     110                void previsit( const ast::VoidType * ) { visit_children = false; }
     111                void previsit( const ast::BasicType * ) { visit_children = false; }
     112                void previsit( const ast::PointerType * ) { visit_children = false; }
     113                void previsit( const ast::ArrayType * ) { visit_children = false; }
     114                void previsit( const ast::FunctionType * ) { visit_children = false; }
     115                void previsit( const ast::StructInstType * ) { visit_children = false; }
     116                void previsit( const ast::UnionInstType * ) { visit_children = false; }
     117                void previsit( const ast::EnumInstType * ) { visit_children = false; }
     118                void previsit( const ast::TraitInstType * ) { visit_children = false; }
     119                void previsit( const ast::TypeInstType * ) { visit_children = false; }
     120                void previsit( const ast::TupleType * ) { visit_children = false; }
     121                void previsit( const ast::VarArgsType * ) { visit_children = false; }
     122                void previsit( const ast::ZeroType * ) { visit_children = false; }
     123                void previsit( const ast::OneType * ) { visit_children = false; }
    123124
    124                 const ast::Type * postmutate( const ast::ArrayType * at ) {
     125                const ast::Type * postvisit( const ast::ArrayType * at ) {
    125126                        return new ast::PointerType{ at->base, at->qualifiers };
    126127                }
    127128
    128                 const ast::Type * postmutate( const ast::FunctionType * ft ) {
     129                const ast::Type * postvisit( const ast::FunctionType * ft ) {
    129130                        return new ast::PointerType{ ft };
    130131                }
    131132
    132                 const ast::Type * postmutate( const ast::TypeInstType * inst ) {
     133                const ast::Type * postvisit( const ast::TypeInstType * inst ) {
    133134                        // replace known function-type-variables with pointer-to-function
    134                         if ( const ast::EqvClass * eqvClass = tenv.lookup( inst->name ) ) {
     135                        if ( const ast::EqvClass * eqvClass = tenv.lookup( *inst ) ) {
    135136                                if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
    136137                                        return new ast::PointerType{ inst };
  • src/ResolvExpr/AlternativeFinder.cc

    r3c64c668 r58fe85a  
    131131
    132132        void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt ) {
    133                 Indenter indent = { indentAmt };
    134                 for ( AltList::const_iterator i = list.begin(); i != list.end(); ++i ) {
    135                         i->print( os, indent );
    136                         os << std::endl;
     133                std::vector<std::string> sorted;
     134                sorted.reserve(list.size());
     135                for(const auto & c : list) {
     136                        std::stringstream ss;
     137                        c.print( ss, indentAmt );
     138                        sorted.push_back(ss.str());
     139                }
     140
     141                std::sort(sorted.begin(), sorted.end());
     142
     143                for ( const auto & s : sorted ) {
     144                        os << s << std::endl;
    137145                }
    138146        }
     
    251259                        SemanticError( expr, "No reasonable alternatives for expression " );
    252260                }
    253                 if ( mode.satisfyAssns || mode.prune ) {
     261                if ( mode.prune ) {
    254262                        // trim candidates just to those where the assertions resolve
    255263                        // - necessary pre-requisite to pruning
     
    12161224                        unify( castExpr->result, alt.expr->result, alt.env, needAssertions,
    12171225                                haveAssertions, openVars, indexer );
    1218                         Cost thisCost = castCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(),
    1219                                 indexer, alt.env );
     1226                        Cost thisCost =
     1227                                castExpr->isGenerated
     1228                                ? conversionCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(),   indexer, alt.env )
     1229                                : castCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(), indexer, alt.env );
    12201230                        PRINT(
    12211231                                std::cerr << "working on cast with result: " << castExpr->result << std::endl;
     
    16981708
    16991709                                // unification run for side-effects
    1700                                 unify( toType, alt.expr->result, newEnv, need, have, openVars, indexer );
     1710                                bool canUnify = unify( toType, alt.expr->result, newEnv, need, have, openVars, indexer );
     1711                                (void) canUnify;
    17011712                                // xxx - do some inspecting on this line... why isn't result bound to initAlt.type?
    17021713
    1703                                 Cost thisCost = castCost( alt.expr->result, toType, alt.expr->get_lvalue(),
     1714                                Cost thisCost = computeConversionCost( alt.expr->result, toType, alt.expr->get_lvalue(),
    17041715                                        indexer, newEnv );
     1716
     1717                                PRINT(
     1718                                        Cost legacyCost = castCost( alt.expr->result, toType, alt.expr->get_lvalue(),
     1719                                                indexer, newEnv );
     1720                                        std::cerr << "Considering initialization:";
     1721                                        std::cerr << std::endl << "  FROM: "; alt.expr->result->print(std::cerr);
     1722                                        std::cerr << std::endl << "  TO: ";   toType          ->print(std::cerr);
     1723                                        std::cerr << std::endl << "  Unification " << (canUnify ? "succeeded" : "failed");
     1724                                        std::cerr << std::endl << "  Legacy cost " << legacyCost;
     1725                                        std::cerr << std::endl << "  New cost " << thisCost;
     1726                                        std::cerr << std::endl;
     1727                                )
     1728
    17051729                                if ( thisCost != Cost::infinity ) {
    17061730                                        // count one safe conversion for each value that is thrown away
  • src/ResolvExpr/Candidate.cpp

    r3c64c668 r58fe85a  
    4141
    4242void print( std::ostream & os, const CandidateList & cands, Indenter indent ) {
    43         for ( const CandidateRef & cand : cands ) {
    44                 print( os, *cand, indent );
    45                 os << std::endl;
     43        std::vector<std::string> sorted;
     44        sorted.reserve(cands.size());
     45        for(const auto & c : cands) {
     46                std::stringstream ss;
     47                print( ss, *c, indent );
     48                sorted.push_back(ss.str());
     49        }
     50
     51        std::sort(sorted.begin(), sorted.end());
     52
     53        for ( const auto & s : sorted ) {
     54                os << s << std::endl;
    4655        }
    4756}
  • src/ResolvExpr/Candidate.hpp

    r3c64c668 r58fe85a  
    5151
    5252        Candidate( const ast::Expr * x, const ast::TypeEnvironment & e )
    53         : expr( x ), cost( Cost::zero ), cvtCost( Cost::zero ), env( e ), open(), need() {}
     53        : expr( x ), cost( Cost::zero ), cvtCost( Cost::zero ), env( e ), open(), need() {
     54                assert(x->result);
     55        }
    5456
    5557        Candidate( const Candidate & o, const ast::Expr * x, const Cost & addedCost = Cost::zero )
    5658        : expr( x ), cost( o.cost + addedCost ), cvtCost( Cost::zero ), env( o.env ), open( o.open ),
    57           need( o.need ) {}
     59          need( o.need ) {
     60                assert(x->result);
     61        }
    5862
    5963        Candidate(
    60                 const ast::Expr * x, const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 
     64                const ast::Expr * x, const ast::TypeEnvironment & e, const ast::OpenVarSet & o,
    6165                const ast::AssertionSet & n, const Cost & c, const Cost & cvt = Cost::zero )
    62         : expr( x ), cost( c ), cvtCost( cvt ), env( e ), open( o ), need( n.begin(), n.end() ) {}
     66        : expr( x ), cost( c ), cvtCost( cvt ), env( e ), open( o ), need( n.begin(), n.end() ) {
     67                assert(x->result);
     68        }
    6369
    6470        Candidate(
     
    6672                ast::AssertionSet && n, const Cost & c, const Cost & cvt = Cost::zero )
    6773        : expr( x ), cost( c ), cvtCost( cvt ), env( std::move( e ) ), open( std::move( o ) ),
    68           need( n.begin(), n.end() ) {}
     74          need( n.begin(), n.end() ) {
     75                assert(x->result);
     76        }
    6977};
    7078
  • src/ResolvExpr/CandidateFinder.cpp

    r3c64c668 r58fe85a  
    99// Author           : Aaron B. Moss
    1010// Created On       : Wed Jun 5 14:30:00 2019
    11 // Last Modified By : Aaron B. Moss
    12 // Last Modified On : Wed Jun 5 14:30:00 2019
    13 // Update Count     : 1
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Oct  1 14:55:00 2019
     13// Update Count     : 2
    1414//
    1515
     
    4343#include "SymTab/Validate.h"      // for validateType
    4444#include "Tuples/Tuples.h"        // for handleTupleAssignment
     45#include "InitTweak/InitTweak.h"  // for getPointerBase
     46
     47#include "Common/Stats/Counter.h"
    4548
    4649#define PRINT( text ) if ( resolvep ) { text }
     
    5457                return new ast::CastExpr{ expr, expr->result->stripReferences() };
    5558        }
    56        
     59
    5760        return expr;
    5861}
     
    6164UniqueId globalResnSlot = 0;
    6265
    63 Cost computeConversionCost( 
    64         const ast::Type * argType, const ast::Type * paramType, const ast::SymbolTable & symtab,
    65         const ast::TypeEnvironment & env
     66Cost computeConversionCost(
     67        const ast::Type * argType, const ast::Type * paramType, bool argIsLvalue,
     68        const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
    6669) {
    6770        PRINT(
     
    7477                std::cerr << std::endl;
    7578        )
    76         Cost convCost = conversionCost( argType, paramType, symtab, env );
     79        Cost convCost = conversionCost( argType, paramType, argIsLvalue, symtab, env );
    7780        PRINT(
    7881                std::cerr << std::endl << "cost is " << convCost << std::endl;
     
    107110
    108111        /// Computes conversion cost for a given expression to a given type
    109         const ast::Expr * computeExpressionConversionCost( 
    110                 const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost 
     112        const ast::Expr * computeExpressionConversionCost(
     113                const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost
    111114        ) {
    112                 Cost convCost = computeConversionCost( arg->result, paramType, symtab, env );
     115                Cost convCost = computeConversionCost(
     116                                arg->result, paramType, arg->get_lvalue(), symtab, env );
    113117                outCost += convCost;
    114118
    115                 // If there is a non-zero conversion cost, ignoring poly cost, then the expression requires 
    116                 // conversion. Ignore poly cost for now, since this requires resolution of the cast to 
     119                // If there is a non-zero conversion cost, ignoring poly cost, then the expression requires
     120                // conversion. Ignore poly cost for now, since this requires resolution of the cast to
    117121                // infer parameters and this does not currently work for the reason stated below
    118122                Cost tmpCost = convCost;
     
    123127                        return new ast::CastExpr{ arg, newType };
    124128
    125                         // xxx - *should* be able to resolve this cast, but at the moment pointers are not 
    126                         // castable to zero_t, but are implicitly convertible. This is clearly inconsistent, 
     129                        // xxx - *should* be able to resolve this cast, but at the moment pointers are not
     130                        // castable to zero_t, but are implicitly convertible. This is clearly inconsistent,
    127131                        // once this is fixed it should be possible to resolve the cast.
    128                         // xxx - this isn't working, it appears because type1 (parameter) is seen as widenable, 
    129                         // but it shouldn't be because this makes the conversion from DT* to DT* since 
     132                        // xxx - this isn't working, it appears because type1 (parameter) is seen as widenable,
     133                        // but it shouldn't be because this makes the conversion from DT* to DT* since
    130134                        // commontype(zero_t, DT*) is DT*, rather than nothing
    131135
    132136                        // CandidateFinder finder{ symtab, env };
    133137                        // finder.find( arg, ResolvMode::withAdjustment() );
    134                         // assertf( finder.candidates.size() > 0, 
     138                        // assertf( finder.candidates.size() > 0,
    135139                        //      "Somehow castable expression failed to find alternatives." );
    136                         // assertf( finder.candidates.size() == 1, 
     140                        // assertf( finder.candidates.size() == 1,
    137141                        //      "Somehow got multiple alternatives for known cast expression." );
    138142                        // return finder.candidates.front()->expr;
     
    143147
    144148        /// Computes conversion cost for a given candidate
    145         Cost computeApplicationConversionCost( 
    146                 CandidateRef cand, const ast::SymbolTable & symtab 
     149        Cost computeApplicationConversionCost(
     150                CandidateRef cand, const ast::SymbolTable & symtab
    147151        ) {
    148152                auto appExpr = cand->expr.strict_as< ast::ApplicationExpr >();
     
    167171                                if ( function->isVarArgs ) {
    168172                                        convCost.incUnsafe();
    169                                         PRINT( std::cerr << "end of params with varargs function: inc unsafe: " 
     173                                        PRINT( std::cerr << "end of params with varargs function: inc unsafe: "
    170174                                                << convCost << std::endl; ; )
    171175                                        // convert reference-typed expressions into value-typed expressions
    172                                         cand->expr = ast::mutate_field_index( 
    173                                                 appExpr, &ast::ApplicationExpr::args, i, 
     176                                        cand->expr = ast::mutate_field_index(
     177                                                appExpr, &ast::ApplicationExpr::args, i,
    174178                                                referenceToRvalueConversion( args[i], convCost ) );
    175179                                        continue;
     
    180184                                // Default arguments should be free - don't include conversion cost.
    181185                                // Unwrap them here because they are not relevant to the rest of the system
    182                                 cand->expr = ast::mutate_field_index( 
     186                                cand->expr = ast::mutate_field_index(
    183187                                        appExpr, &ast::ApplicationExpr::args, i, def->expr );
    184188                                ++param;
     
    187191
    188192                        // mark conversion cost and also specialization cost of param type
    189                         const ast::Type * paramType = (*param)->get_type();
    190                         cand->expr = ast::mutate_field_index( 
    191                                 appExpr, &ast::ApplicationExpr::args, i, 
    192                                 computeExpressionConversionCost( 
    193                                         args[i], paramType, symtab, cand->env, convCost ) );
    194                         convCost.decSpec( specCost( paramType ) );
     193                        // const ast::Type * paramType = (*param)->get_type();
     194                        cand->expr = ast::mutate_field_index(
     195                                appExpr, &ast::ApplicationExpr::args, i,
     196                                computeExpressionConversionCost(
     197                                        args[i], *param, symtab, cand->env, convCost ) );
     198                        convCost.decSpec( specCost( *param ) );
    195199                        ++param;  // can't be in for-loop update because of the continue
    196200                }
     
    198202                if ( param != params.end() ) return Cost::infinity;
    199203
    200                 // specialization cost of return types can't be accounted for directly, it disables 
     204                // specialization cost of return types can't be accounted for directly, it disables
    201205                // otherwise-identical calls, like this example based on auto-newline in the I/O lib:
    202206                //
     
    208212                // mark type variable and specialization cost of forall clause
    209213                convCost.incVar( function->forall.size() );
    210                 for ( const ast::TypeDecl * td : function->forall ) {
    211                         convCost.decSpec( td->assertions.size() );
    212                 }
     214                convCost.decSpec( function->assertions.size() );
    213215
    214216                return convCost;
    215217        }
    216218
    217         void makeUnifiableVars( 
    218                 const ast::ParameterizedType * type, ast::OpenVarSet & unifiableVars,
    219                 ast::AssertionSet & need 
     219        void makeUnifiableVars(
     220                const ast::FunctionType * type, ast::OpenVarSet & unifiableVars,
     221                ast::AssertionSet & need
    220222        ) {
    221                 for ( const ast::TypeDecl * tyvar : type->forall ) {
    222                         unifiableVars[ tyvar->name ] = ast::TypeDecl::Data{ tyvar };
    223                         for ( const ast::DeclWithType * assn : tyvar->assertions ) {
    224                                 need[ assn ].isUsed = true;
    225                         }
     223                for ( auto & tyvar : type->forall ) {
     224                        unifiableVars[ *tyvar ] = ast::TypeDecl::Data{ tyvar->base };
     225                }
     226                for ( auto & assn : type->assertions ) {
     227                        need[ assn ].isUsed = true;
    226228                }
    227229        }
     
    254256
    255257                ArgPack()
    256                 : parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ), 
     258                : parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ),
    257259                  tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {}
    258                
    259                 ArgPack( 
    260                         const ast::TypeEnvironment & env, const ast::AssertionSet & need, 
     260
     261                ArgPack(
     262                        const ast::TypeEnvironment & env, const ast::AssertionSet & need,
    261263                        const ast::AssertionSet & have, const ast::OpenVarSet & open )
    262                 : parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ), 
     264                : parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ),
    263265                  open( open ), nextArg( 0 ), tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {}
    264                
     266
    265267                ArgPack(
    266                         std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env, 
    267                         ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open, 
    268                         unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero, 
     268                        std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env,
     269                        ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open,
     270                        unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero,
    269271                        unsigned nextExpl = 0, unsigned explAlt = 0 )
    270272                : parent(parent), expr( expr ), cost( cost ), env( move( env ) ), need( move( need ) ),
    271273                  have( move( have ) ), open( move( open ) ), nextArg( nextArg ), tupleStart( tupleStart ),
    272274                  nextExpl( nextExpl ), explAlt( explAlt ) {}
    273                
     275
    274276                ArgPack(
    275                         const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need, 
     277                        const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need,
    276278                        ast::AssertionSet && have, ast::OpenVarSet && open, unsigned nextArg, Cost added )
    277                 : parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( move( env ) ), 
    278                   need( move( need ) ), have( move( have ) ), open( move( open ) ), nextArg( nextArg ), 
     279                : parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( move( env ) ),
     280                  need( move( need ) ), have( move( have ) ), open( move( open ) ), nextArg( nextArg ),
    279281                  tupleStart( o.tupleStart ), nextExpl( 0 ), explAlt( 0 ) {}
    280                
     282
    281283                /// true if this pack is in the middle of an exploded argument
    282284                bool hasExpl() const { return nextExpl > 0; }
     
    286288                        return args[ nextArg-1 ][ explAlt ];
    287289                }
    288                
     290
    289291                /// Ends a tuple expression, consolidating the appropriate args
    290292                void endTuple( const std::vector< ArgPack > & packs ) {
     
    307309
    308310        /// Instantiates an argument to match a parameter, returns false if no matching results left
    309         bool instantiateArgument( 
    310                 const ast::Type * paramType, const ast::Init * init, const ExplodedArgs_new & args, 
    311                 std::vector< ArgPack > & results, std::size_t & genStart, const ast::SymbolTable & symtab, 
    312                 unsigned nTuples = 0 
     311        bool instantiateArgument(
     312                const ast::Type * paramType, const ast::Init * init, const ExplodedArgs_new & args,
     313                std::vector< ArgPack > & results, std::size_t & genStart, const ast::SymbolTable & symtab,
     314                unsigned nTuples = 0
    313315        ) {
    314316                if ( auto tupleType = dynamic_cast< const ast::TupleType * >( paramType ) ) {
     
    318320                                // xxx - dropping initializer changes behaviour from previous, but seems correct
    319321                                // ^^^ need to handle the case where a tuple has a default argument
    320                                 if ( ! instantiateArgument( 
     322                                if ( ! instantiateArgument(
    321323                                        type, nullptr, args, results, genStart, symtab, nTuples ) ) return false;
    322324                                nTuples = 0;
     
    329331                } else if ( const ast::TypeInstType * ttype = Tuples::isTtype( paramType ) ) {
    330332                        // paramType is a ttype, consumes all remaining arguments
    331                        
     333
    332334                        // completed tuples; will be spliced to end of results to finish
    333335                        std::vector< ArgPack > finalResults{};
     
    342344                                for ( std::size_t i = genStart; i < genEnd; ++i ) {
    343345                                        unsigned nextArg = results[i].nextArg;
    344                                        
     346
    345347                                        // use next element of exploded tuple if present
    346348                                        if ( results[i].hasExpl() ) {
     
    352354                                                results.emplace_back(
    353355                                                        i, expl.exprs[ results[i].nextExpl ], copy( results[i].env ),
    354                                                         copy( results[i].need ), copy( results[i].have ), 
     356                                                        copy( results[i].need ), copy( results[i].have ),
    355357                                                        copy( results[i].open ), nextArg, nTuples, Cost::zero, nextExpl,
    356358                                                        results[i].explAlt );
     
    370372                                                        // push empty tuple expression
    371373                                                        newResult.parent = i;
    372                                                         std::vector< ast::ptr< ast::Expr > > emptyList;
    373                                                         newResult.expr =
    374                                                                 new ast::TupleExpr{ CodeLocation{}, move( emptyList ) };
     374                                                        newResult.expr = new ast::TupleExpr{ CodeLocation{}, {} };
    375375                                                        argType = newResult.expr->result;
    376376                                                } else {
     
    400400
    401401                                                // check unification for ttype before adding to final
    402                                                 if ( 
    403                                                         unify( 
     402                                                if (
     403                                                        unify(
    404404                                                                ttype, argType, newResult.env, newResult.need, newResult.have,
    405                                                                 newResult.open, symtab ) 
     405                                                                newResult.open, symtab )
    406406                                                ) {
    407407                                                        finalResults.emplace_back( move( newResult ) );
     
    424424                                                if ( expl.exprs.empty() ) {
    425425                                                        results.emplace_back(
    426                                                                 results[i], move( env ), copy( results[i].need ), 
     426                                                                results[i], move( env ), copy( results[i].need ),
    427427                                                                copy( results[i].have ), move( open ), nextArg + 1, expl.cost );
    428                                                        
     428
    429429                                                        continue;
    430430                                                }
     
    432432                                                // add new result
    433433                                                results.emplace_back(
    434                                                         i, expl.exprs.front(), move( env ), copy( results[i].need ), 
    435                                                         copy( results[i].have ), move( open ), nextArg + 1, nTuples, 
     434                                                        i, expl.exprs.front(), move( env ), copy( results[i].need ),
     435                                                        copy( results[i].have ), move( open ), nextArg + 1, nTuples,
    436436                                                        expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
    437437                                        }
     
    479479
    480480                                        results.emplace_back(
    481                                                 i, expr, move( env ), move( need ), move( have ), move( open ), nextArg, 
     481                                                i, expr, move( env ), move( need ), move( have ), move( open ), nextArg,
    482482                                                nTuples, Cost::zero, nextExpl, results[i].explAlt );
    483483                                }
     
    495495                                        if ( unify( paramType, cnst->result, env, need, have, open, symtab ) ) {
    496496                                                results.emplace_back(
    497                                                         i, new ast::DefaultArgExpr{ cnst->location, cnst }, move( env ), 
     497                                                        i, new ast::DefaultArgExpr{ cnst->location, cnst }, move( env ),
    498498                                                        move( need ), move( have ), move( open ), nextArg, nTuples );
    499499                                        }
     
    517517                                if ( expl.exprs.empty() ) {
    518518                                        results.emplace_back(
    519                                                 results[i], move( env ), move( need ), move( have ), move( open ), 
     519                                                results[i], move( env ), move( need ), move( have ), move( open ),
    520520                                                nextArg + 1, expl.cost );
    521                                        
     521
    522522                                        continue;
    523523                                }
     
    539539                                        // add new result
    540540                                        results.emplace_back(
    541                                                 i, expr, move( env ), move( need ), move( have ), move( open ), 
     541                                                i, expr, move( env ), move( need ), move( have ), move( open ),
    542542                                                nextArg + 1, nTuples, expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
    543543                                }
     
    548548                genStart = genEnd;
    549549
    550                 return genEnd != results.size();
     550                return genEnd != results.size();  // were any new results added?
    551551        }
    552552
    553553        /// Generate a cast expression from `arg` to `toType`
    554         const ast::Expr * restructureCast( 
     554        const ast::Expr * restructureCast(
    555555                ast::ptr< ast::Expr > & arg, const ast::Type * toType, ast::GeneratedFlag isGenerated = ast::GeneratedCast
    556556        ) {
    557                 if ( 
    558                         arg->result->size() > 1 
    559                         && ! toType->isVoid() 
    560                         && ! dynamic_cast< const ast::ReferenceType * >( toType ) 
     557                if (
     558                        arg->result->size() > 1
     559                        && ! toType->isVoid()
     560                        && ! dynamic_cast< const ast::ReferenceType * >( toType )
    561561                ) {
    562                         // Argument is a tuple and the target type is neither void nor a reference. Cast each 
    563                         // member of the tuple to its corresponding target type, producing the tuple of those 
    564                         // cast expressions. If there are more components of the tuple than components in the 
    565                         // target type, then excess components do not come out in the result expression (but 
     562                        // Argument is a tuple and the target type is neither void nor a reference. Cast each
     563                        // member of the tuple to its corresponding target type, producing the tuple of those
     564                        // cast expressions. If there are more components of the tuple than components in the
     565                        // target type, then excess components do not come out in the result expression (but
    566566                        // UniqueExpr ensures that the side effects will still be produced)
    567567                        if ( Tuples::maybeImpureIgnoreUnique( arg ) ) {
    568                                 // expressions which may contain side effects require a single unique instance of 
     568                                // expressions which may contain side effects require a single unique instance of
    569569                                // the expression
    570570                                arg = new ast::UniqueExpr{ arg->location, arg };
     
    574574                                // cast each component
    575575                                ast::ptr< ast::Expr > idx = new ast::TupleIndexExpr{ arg->location, arg, i };
    576                                 components.emplace_back( 
     576                                components.emplace_back(
    577577                                        restructureCast( idx, toType->getComponent( i ), isGenerated ) );
    578578                        }
     
    594594
    595595        /// Actually visits expressions to find their candidate interpretations
    596         struct Finder final : public ast::WithShortCircuiting {
     596        class Finder final : public ast::WithShortCircuiting {
     597                const ast::SymbolTable & symtab;
     598        public:
     599                static size_t traceId;
    597600                CandidateFinder & selfFinder;
    598                 const ast::SymbolTable & symtab;
    599601                CandidateList & candidates;
    600602                const ast::TypeEnvironment & tenv;
    601603                ast::ptr< ast::Type > & targetType;
    602604
     605                enum Errors {
     606                        NotFound,
     607                        NoMatch,
     608                        ArgsToFew,
     609                        ArgsToMany,
     610                        RetsToFew,
     611                        RetsToMany,
     612                        NoReason
     613                };
     614
     615                struct {
     616                        Errors code = NotFound;
     617                } reason;
     618
    603619                Finder( CandidateFinder & f )
    604                 : selfFinder( f ), symtab( f.symtab ), candidates( f.candidates ), tenv( f.env ),
     620                : symtab( f.localSyms ), selfFinder( f ), candidates( f.candidates ), tenv( f.env ),
    605621                  targetType( f.targetType ) {}
    606                
     622
    607623                void previsit( const ast::Node * ) { visit_children = false; }
    608624
     
    611627                void addCandidate( Args &&... args ) {
    612628                        candidates.emplace_back( new Candidate{ std::forward<Args>( args )... } );
     629                        reason.code = NoReason;
    613630                }
    614631
     
    639656
    640657                /// Completes a function candidate with arguments located
    641                 void validateFunctionCandidate( 
    642                         const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results, 
    643                         CandidateList & out 
     658                void validateFunctionCandidate(
     659                        const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results,
     660                        CandidateList & out
    644661                ) {
    645                         ast::ApplicationExpr * appExpr = 
     662                        ast::ApplicationExpr * appExpr =
    646663                                new ast::ApplicationExpr{ func->expr->location, func->expr };
    647664                        // sum cost and accumulate arguments
     
    657674                        appExpr->args = move( vargs );
    658675                        // build and validate new candidate
    659                         auto newCand = 
     676                        auto newCand =
    660677                                std::make_shared<Candidate>( appExpr, result.env, result.open, result.need, cost );
    661678                        PRINT(
     
    669686                /// Builds a list of candidates for a function, storing them in out
    670687                void makeFunctionCandidates(
    671                         const CandidateRef & func, const ast::FunctionType * funcType, 
     688                        const CandidateRef & func, const ast::FunctionType * funcType,
    672689                        const ExplodedArgs_new & args, CandidateList & out
    673690                ) {
     
    676693                        ast::TypeEnvironment funcEnv{ func->env };
    677694                        makeUnifiableVars( funcType, funcOpen, funcNeed );
    678                         // add all type variables as open variables now so that those not used in the parameter
    679                         // list are still considered open
     695                        // add all type variables as open variables now so that those not used in the
     696                        // parameter list are still considered open
    680697                        funcEnv.add( funcType->forall );
    681698
    682699                        if ( targetType && ! targetType->isVoid() && ! funcType->returns.empty() ) {
    683700                                // attempt to narrow based on expected target type
    684                                 const ast::Type * returnType = funcType->returns.front()->get_type();
    685                                 if ( ! unify( 
    686                                         returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab ) 
     701                                const ast::Type * returnType = funcType->returns.front();
     702                                if ( ! unify(
     703                                        returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab )
    687704                                ) {
    688705                                        // unification failed, do not pursue this candidate
     
    696713                        std::size_t genStart = 0;
    697714
    698                         for ( const ast::DeclWithType * param : funcType->params ) {
    699                                 auto obj = strict_dynamic_cast< const ast::ObjectDecl * >( param );
    700                                 // Try adding the arguments corresponding to the current parameter to the existing
     715                        // xxx - how to handle default arg after change to ftype representation?
     716                        if (const ast::VariableExpr * varExpr = func->expr.as<ast::VariableExpr>()) {
     717                                if (const ast::FunctionDecl * funcDecl = varExpr->var.as<ast::FunctionDecl>()) {
     718                                        // function may have default args only if directly calling by name
     719                                        // must use types on candidate however, due to RenameVars substitution
     720                                        auto nParams = funcType->params.size();
     721
     722                                        for (size_t i=0; i<nParams; ++i) {
     723                                                auto obj = funcDecl->params[i].strict_as<ast::ObjectDecl>();
     724                                                if (!instantiateArgument(
     725                                                        funcType->params[i], obj->init, args, results, genStart, symtab)) return;
     726                                        }
     727                                        goto endMatch;
     728                                }
     729                        }
     730                        for ( const auto & param : funcType->params ) {
     731                                // Try adding the arguments corresponding to the current parameter to the existing
    701732                                // matches
    702                                 if ( ! instantiateArgument(
    703                                         obj->type, obj->init, args, results, genStart, symtab ) ) return;
    704                         }
    705 
     733                                // no default args for indirect calls
     734                                if ( ! instantiateArgument(
     735                                        param, nullptr, args, results, genStart, symtab ) ) return;
     736                        }
     737
     738                        endMatch:
    706739                        if ( funcType->isVarArgs ) {
    707740                                // append any unused arguments to vararg pack
     
    750783                                                        if ( expl.exprs.empty() ) {
    751784                                                                results.emplace_back(
    752                                                                         results[i], move( env ), copy( results[i].need ), 
    753                                                                         copy( results[i].have ), move( open ), nextArg + 1, 
     785                                                                        results[i], move( env ), copy( results[i].need ),
     786                                                                        copy( results[i].have ), move( open ), nextArg + 1,
    754787                                                                        expl.cost );
    755788
     
    760793                                                        results.emplace_back(
    761794                                                                i, expl.exprs.front(), move( env ), copy( results[i].need ),
    762                                                                 copy( results[i].have ), move( open ), nextArg + 1, 0, expl.cost, 
     795                                                                copy( results[i].have ), move( open ), nextArg + 1, 0, expl.cost,
    763796                                                                expl.exprs.size() == 1 ? 0 : 1, j );
    764797                                                }
     
    780813                /// Adds implicit struct-conversions to the alternative list
    781814                void addAnonConversions( const CandidateRef & cand ) {
    782                         // adds anonymous member interpretations whenever an aggregate value type is seen. 
    783                         // it's okay for the aggregate expression to have reference type -- cast it to the 
     815                        // adds anonymous member interpretations whenever an aggregate value type is seen.
     816                        // it's okay for the aggregate expression to have reference type -- cast it to the
    784817                        // base type to treat the aggregate as the referenced value
    785818                        ast::ptr< ast::Expr > aggrExpr( cand->expr );
    786819                        ast::ptr< ast::Type > & aggrType = aggrExpr.get_and_mutate()->result;
    787820                        cand->env.apply( aggrType );
    788                        
     821
    789822                        if ( aggrType.as< ast::ReferenceType >() ) {
    790823                                aggrExpr = new ast::CastExpr{ aggrExpr, aggrType->stripReferences() };
     
    799832
    800833                /// Adds aggregate member interpretations
    801                 void addAggMembers( 
    802                         const ast::ReferenceToType * aggrInst, const ast::Expr * expr,
    803                         const Candidate & cand, const Cost & addedCost, const std::string & name 
     834                void addAggMembers(
     835                        const ast::BaseInstType * aggrInst, const ast::Expr * expr,
     836                        const Candidate & cand, const Cost & addedCost, const std::string & name
    804837                ) {
    805838                        for ( const ast::Decl * decl : aggrInst->lookup( name ) ) {
    806839                                auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( decl );
    807                                 CandidateRef newCand = std::make_shared<Candidate>( 
     840                                CandidateRef newCand = std::make_shared<Candidate>(
    808841                                        cand, new ast::MemberExpr{ expr->location, dwt, expr }, addedCost );
    809                                 // add anonymous member interpretations whenever an aggregate value type is seen 
     842                                // add anonymous member interpretations whenever an aggregate value type is seen
    810843                                // as a member expression
    811844                                addAnonConversions( newCand );
     
    815848
    816849                /// Adds tuple member interpretations
    817                 void addTupleMembers( 
    818                         const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand, 
    819                         const Cost & addedCost, const ast::Expr * member 
     850                void addTupleMembers(
     851                        const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand,
     852                        const Cost & addedCost, const ast::Expr * member
    820853                ) {
    821854                        if ( auto constantExpr = dynamic_cast< const ast::ConstantExpr * >( member ) ) {
    822                                 // get the value of the constant expression as an int, must be between 0 and the 
     855                                // get the value of the constant expression as an int, must be between 0 and the
    823856                                // length of the tuple to have meaning
    824857                                long long val = constantExpr->intValue();
    825858                                if ( val >= 0 && (unsigned long long)val < tupleType->size() ) {
    826859                                        addCandidate(
    827                                                 cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val }, 
     860                                                cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val },
    828861                                                addedCost );
    829862                                }
     
    832865
    833866                void postvisit( const ast::UntypedExpr * untypedExpr ) {
    834                         CandidateFinder funcFinder{ symtab, tenv };
    835                         funcFinder.find( untypedExpr->func, ResolvMode::withAdjustment() );
    836                         // short-circuit if no candidates
    837                         if ( funcFinder.candidates.empty() ) return;
    838 
    839                         std::vector< CandidateFinder > argCandidates =
     867                        std::vector< CandidateFinder > argCandidates =
    840868                                selfFinder.findSubExprs( untypedExpr->args );
    841                        
     869
    842870                        // take care of possible tuple assignments
    843871                        // if not tuple assignment, handled as normal function call
    844872                        Tuples::handleTupleAssignment( selfFinder, untypedExpr, argCandidates );
     873
     874                        CandidateFinder funcFinder{ symtab, tenv };
     875                        if (auto nameExpr = untypedExpr->func.as<ast::NameExpr>()) {
     876                                auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
     877                                if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {
     878                                        assertf(!argCandidates.empty(), "special function call without argument");
     879                                        for (auto & firstArgCand: argCandidates[0]) {
     880                                                ast::ptr<ast::Type> argType = firstArgCand->expr->result;
     881                                                firstArgCand->env.apply(argType);
     882                                                // strip references
     883                                                // xxx - is this correct?
     884                                                while (argType.as<ast::ReferenceType>()) argType = argType.as<ast::ReferenceType>()->base;
     885
     886                                                // convert 1-tuple to plain type
     887                                                if (auto tuple = argType.as<ast::TupleType>()) {
     888                                                        if (tuple->size() == 1) {
     889                                                                argType = tuple->types[0];
     890                                                        }
     891                                                }
     892                                               
     893                                                // if argType is an unbound type parameter, all special functions need to be searched.
     894                                                if (isUnboundType(argType)) {
     895                                                        funcFinder.otypeKeys.clear();
     896                                                        break;
     897                                                }
     898
     899                                                if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer);
     900                                                else funcFinder.otypeKeys.insert(Mangle::mangle(argType, Mangle::NoGenericParams | Mangle::Type));
     901                                        }
     902                                }
     903                        }
     904                        // if candidates are already produced, do not fail
     905                        // xxx - is it possible that handleTupleAssignment and main finder both produce candidates?
     906                        // this means there exists ctor/assign functions with a tuple as first parameter.
     907                        ResolvMode mode = {
     908                                true, // adjust
     909                                !untypedExpr->func.as<ast::NameExpr>(), // prune if not calling by name
     910                                selfFinder.candidates.empty() // failfast if other options are not found
     911                        };
     912                        funcFinder.find( untypedExpr->func, mode );
     913                        // short-circuit if no candidates
     914                        // if ( funcFinder.candidates.empty() ) return;
     915
     916                        reason.code = NoMatch;
    845917
    846918                        // find function operators
     
    877949                                                if ( auto function = pointer->base.as< ast::FunctionType >() ) {
    878950                                                        CandidateRef newFunc{ new Candidate{ *func } };
    879                                                         newFunc->expr = 
     951                                                        newFunc->expr =
    880952                                                                referenceToRvalueConversion( newFunc->expr, newFunc->cost );
    881953                                                        makeFunctionCandidates( newFunc, function, argExpansions, found );
    882954                                                }
    883                                         } else if ( 
    884                                                 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult ) 
     955                                        } else if (
     956                                                auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult )
    885957                                        ) {
    886                                                 if ( const ast::EqvClass * clz = func->env.lookup( inst->name ) ) {
     958                                                if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) {
    887959                                                        if ( auto function = clz->bound.as< ast::FunctionType >() ) {
    888960                                                                CandidateRef newFunc{ new Candidate{ *func } };
    889                                                                 newFunc->expr = 
     961                                                                newFunc->expr =
    890962                                                                        referenceToRvalueConversion( newFunc->expr, newFunc->cost );
    891963                                                                makeFunctionCandidates( newFunc, function, argExpansions, found );
     
    901973                                std::vector< ExplodedArg > funcE;
    902974                                funcE.reserve( funcFinder.candidates.size() );
    903                                 for ( const CandidateRef & func : funcFinder ) { 
     975                                for ( const CandidateRef & func : funcFinder ) {
    904976                                        funcE.emplace_back( *func, symtab );
    905977                                }
     
    913985                                                        if ( auto function = pointer->base.as< ast::FunctionType >() ) {
    914986                                                                CandidateRef newOp{ new Candidate{ *op} };
    915                                                                 newOp->expr = 
     987                                                                newOp->expr =
    916988                                                                        referenceToRvalueConversion( newOp->expr, newOp->cost );
    917989                                                                makeFunctionCandidates( newOp, function, argExpansions, found );
     
    922994                        }
    923995
    924                         // Implement SFINAE; resolution errors are only errors if there aren't any non-error 
     996                        // Implement SFINAE; resolution errors are only errors if there aren't any non-error
    925997                        // candidates
    926998                        if ( found.empty() && ! errors.isEmpty() ) { throw errors; }
     
    9341006                                        auto pointer = appExpr->func->result.strict_as< ast::PointerType >();
    9351007                                        auto function = pointer->base.strict_as< ast::FunctionType >();
    936                                        
     1008
    9371009                                        std::cerr << "Case +++++++++++++ " << appExpr->func << std::endl;
    9381010                                        std::cerr << "parameters are:" << std::endl;
     
    9571029                        promoteCvtCost( winners );
    9581030
    959                         // function may return a struct/union value, in which case we need to add candidates 
    960                         // for implicit conversions to each of the anonymous members, which must happen after 
     1031                        // function may return a struct/union value, in which case we need to add candidates
     1032                        // for implicit conversions to each of the anonymous members, which must happen after
    9611033                        // `findMinCost`, since anon conversions are never the cheapest
    9621034                        for ( const CandidateRef & c : winners ) {
     
    9661038
    9671039                        if ( candidates.empty() && targetType && ! targetType->isVoid() ) {
    968                                 // If resolution is unsuccessful with a target type, try again without, since it 
     1040                                // If resolution is unsuccessful with a target type, try again without, since it
    9691041                                // will sometimes succeed when it wouldn't with a target type binding.
    9701042                                // For example:
     
    9831055                /// true if expression is an lvalue
    9841056                static bool isLvalue( const ast::Expr * x ) {
    985                         return x->result && ( x->result->is_lvalue() || x->result.as< ast::ReferenceType >() );
     1057                        return x->result && ( x->get_lvalue() || x->result.as< ast::ReferenceType >() );
    9861058                }
    9871059
     
    9891061                        CandidateFinder finder{ symtab, tenv };
    9901062                        finder.find( addressExpr->arg );
     1063
     1064                        if( finder.candidates.empty() ) return;
     1065
     1066                        reason.code = NoMatch;
     1067
    9911068                        for ( CandidateRef & r : finder.candidates ) {
    9921069                                if ( ! isLvalue( r->expr ) ) continue;
     
    10031080                        assert( toType );
    10041081                        toType = resolveTypeof( toType, symtab );
    1005                         toType = SymTab::validateType( castExpr->location, toType, symtab );
     1082                        // toType = SymTab::validateType( castExpr->location, toType, symtab );
    10061083                        toType = adjustExprType( toType, tenv, symtab );
    10071084
    10081085                        CandidateFinder finder{ symtab, tenv, toType };
    10091086                        finder.find( castExpr->arg, ResolvMode::withAdjustment() );
     1087
     1088                        if( !finder.candidates.empty() ) reason.code = NoMatch;
    10101089
    10111090                        CandidateList matches;
     
    10161095                                cand->env.extractOpenVars( open );
    10171096
    1018                                 // It is possible that a cast can throw away some values in a multiply-valued 
    1019                                 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the 
    1020                                 // subexpression results that are cast directly. The candidate is invalid if it 
     1097                                // It is possible that a cast can throw away some values in a multiply-valued
     1098                                // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the
     1099                                // subexpression results that are cast directly. The candidate is invalid if it
    10211100                                // has fewer results than there are types to cast to.
    10221101                                int discardedValues = cand->expr->result->size() - toType->size();
     
    10251104                                // unification run for side-effects
    10261105                                unify( toType, cand->expr->result, cand->env, need, have, open, symtab );
    1027                                 Cost thisCost = castCost( cand->expr->result, toType, symtab, cand->env );
     1106                                Cost thisCost =
     1107                                        (castExpr->isGenerated == ast::GeneratedFlag::GeneratedCast)
     1108                            ? conversionCost( cand->expr->result, toType, cand->expr->get_lvalue(), symtab, cand->env )
     1109                            : castCost( cand->expr->result, toType, cand->expr->get_lvalue(), symtab, cand->env );
     1110
    10281111                                PRINT(
    10291112                                        std::cerr << "working on cast with result: " << toType << std::endl;
     
    10371120                                        // count one safe conversion for each value that is thrown away
    10381121                                        thisCost.incSafe( discardedValues );
    1039                                         CandidateRef newCand = std::make_shared<Candidate>( 
    1040                                                 restructureCast( cand->expr, toType, castExpr->isGenerated ), 
    1041                                                 copy( cand->env ), move( open ), move( need ), cand->cost, 
     1122                                        CandidateRef newCand = std::make_shared<Candidate>(
     1123                                                restructureCast( cand->expr, toType, castExpr->isGenerated ),
     1124                                                copy( cand->env ), move( open ), move( need ), cand->cost,
    10421125                                                cand->cost + thisCost );
    10431126                                        inferParameters( newCand, matches );
     
    10571140                        finder.find( castExpr->arg, ResolvMode::withoutPrune() );
    10581141                        for ( CandidateRef & r : finder.candidates ) {
    1059                                 addCandidate( 
    1060                                         *r, 
     1142                                addCandidate(
     1143                                        *r,
    10611144                                        new ast::VirtualCastExpr{ castExpr->location, r->expr, castExpr->result } );
    10621145                        }
     1146                }
     1147
     1148                void postvisit( const ast::KeywordCastExpr * castExpr ) {
     1149                        const auto & loc = castExpr->location;
     1150                        assertf( castExpr->result, "Cast target should have been set in Validate." );
     1151                        auto ref = castExpr->result.strict_as<ast::ReferenceType>();
     1152                        auto inst = ref->base.strict_as<ast::StructInstType>();
     1153                        auto target = inst->base.get();
     1154
     1155                        CandidateFinder finder{ symtab, tenv };
     1156
     1157                        auto pick_alternatives = [target, this](CandidateList & found, bool expect_ref) {
     1158                                for(auto & cand : found) {
     1159                                        const ast::Type * expr = cand->expr->result.get();
     1160                                        if(expect_ref) {
     1161                                                auto res = dynamic_cast<const ast::ReferenceType*>(expr);
     1162                                                if(!res) { continue; }
     1163                                                expr = res->base.get();
     1164                                        }
     1165
     1166                                        if(auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) {
     1167                                                auto td = cand->env.lookup(*insttype);
     1168                                                if(!td) { continue; }
     1169                                                expr = td->bound.get();
     1170                                        }
     1171
     1172                                        if(auto base = dynamic_cast<const ast::StructInstType*>(expr)) {
     1173                                                if(base->base == target) {
     1174                                                        candidates.push_back( std::move(cand) );
     1175                                                        reason.code = NoReason;
     1176                                                }
     1177                                        }
     1178                                }
     1179                        };
     1180
     1181                        try {
     1182                                // Attempt 1 : turn (thread&)X into ($thread&)X.__thrd
     1183                                // Clone is purely for memory management
     1184                                std::unique_ptr<const ast::Expr> tech1 { new ast::UntypedMemberExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.field), castExpr->arg) };
     1185
     1186                                // don't prune here, since it's guaranteed all alternatives will have the same type
     1187                                finder.find( tech1.get(), ResolvMode::withoutPrune() );
     1188                                pick_alternatives(finder.candidates, false);
     1189
     1190                                return;
     1191                        } catch(SemanticErrorException & ) {}
     1192
     1193                        // Fallback : turn (thread&)X into ($thread&)get_thread(X)
     1194                        std::unique_ptr<const ast::Expr> fallback { ast::UntypedExpr::createDeref(loc,  new ast::UntypedExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.getter), { castExpr->arg })) };
     1195                        // don't prune here, since it's guaranteed all alternatives will have the same type
     1196                        finder.find( fallback.get(), ResolvMode::withoutPrune() );
     1197
     1198                        pick_alternatives(finder.candidates, true);
     1199
     1200                        // Whatever happens here, we have no more fallbacks
    10631201                }
    10641202
     
    10671205                        aggFinder.find( memberExpr->aggregate, ResolvMode::withAdjustment() );
    10681206                        for ( CandidateRef & agg : aggFinder.candidates ) {
    1069                                 // it's okay for the aggregate expression to have reference type -- cast it to the 
     1207                                // it's okay for the aggregate expression to have reference type -- cast it to the
    10701208                                // base type to treat the aggregate as the referenced value
    10711209                                Cost addedCost = Cost::zero;
     
    10741212                                // find member of the given type
    10751213                                if ( auto structInst = agg->expr->result.as< ast::StructInstType >() ) {
    1076                                         addAggMembers( 
     1214                                        addAggMembers(
    10771215                                                structInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
    10781216                                } else if ( auto unionInst = agg->expr->result.as< ast::UnionInstType >() ) {
    1079                                         addAggMembers( 
     1217                                        addAggMembers(
    10801218                                                unionInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
    10811219                                } else if ( auto tupleType = agg->expr->result.as< ast::TupleType >() ) {
     
    10901228
    10911229                void postvisit( const ast::NameExpr * nameExpr ) {
    1092                         std::vector< ast::SymbolTable::IdData > declList = symtab.lookupId( nameExpr->name );
     1230                        std::vector< ast::SymbolTable::IdData > declList;
     1231                        if (!selfFinder.otypeKeys.empty()) {
     1232                                auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
     1233                                assertf(kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS, "special lookup with non-special target: %s", nameExpr->name.c_str());
     1234
     1235                                for (auto & otypeKey: selfFinder.otypeKeys) {
     1236                                        auto result = symtab.specialLookupId(kind, otypeKey);
     1237                                        declList.insert(declList.end(), std::make_move_iterator(result.begin()), std::make_move_iterator(result.end()));
     1238                                }
     1239                        }
     1240                        else {
     1241                                declList = symtab.lookupId( nameExpr->name );
     1242                        }
    10931243                        PRINT( std::cerr << "nameExpr is " << nameExpr->name << std::endl; )
     1244
     1245                        if( declList.empty() ) return;
     1246
     1247                        reason.code = NoMatch;
     1248
    10941249                        for ( auto & data : declList ) {
    10951250                                Cost cost = Cost::zero;
     
    10971252
    10981253                                CandidateRef newCand = std::make_shared<Candidate>(
    1099                                         newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero, 
     1254                                        newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero,
    11001255                                        cost );
    11011256                                PRINT(
     
    11071262                                        std::cerr << std::endl;
    11081263                                )
    1109                                 newCand->expr = ast::mutate_field( 
    1110                                         newCand->expr.get(), &ast::Expr::result, 
     1264                                newCand->expr = ast::mutate_field(
     1265                                        newCand->expr.get(), &ast::Expr::result,
    11111266                                        renameTyVars( newCand->expr->result ) );
    1112                                 // add anonymous member interpretations whenever an aggregate value type is seen 
     1267                                // add anonymous member interpretations whenever an aggregate value type is seen
    11131268                                // as a name expression
    11141269                                addAnonConversions( newCand );
     
    11201275                        // not sufficient to just pass `variableExpr` here, type might have changed since
    11211276                        // creation
    1122                         addCandidate( 
     1277                        addCandidate(
    11231278                                new ast::VariableExpr{ variableExpr->location, variableExpr->var }, tenv );
    11241279                }
     
    11301285                void postvisit( const ast::SizeofExpr * sizeofExpr ) {
    11311286                        if ( sizeofExpr->type ) {
    1132                                 addCandidate( 
    1133                                         new ast::SizeofExpr{ 
    1134                                                 sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) }, 
     1287                                addCandidate(
     1288                                        new ast::SizeofExpr{
     1289                                                sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) },
    11351290                                        tenv );
    11361291                        } else {
     
    11411296                                CandidateList winners = findMinCost( finder.candidates );
    11421297                                if ( winners.size() != 1 ) {
    1143                                         SemanticError( 
     1298                                        SemanticError(
    11441299                                                sizeofExpr->expr.get(), "Ambiguous expression in sizeof operand: " );
    11451300                                }
     
    11541309                void postvisit( const ast::AlignofExpr * alignofExpr ) {
    11551310                        if ( alignofExpr->type ) {
    1156                                 addCandidate( 
    1157                                         new ast::AlignofExpr{ 
    1158                                                 alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) }, 
     1311                                addCandidate(
     1312                                        new ast::AlignofExpr{
     1313                                                alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) },
    11591314                                        tenv );
    11601315                        } else {
     
    11651320                                CandidateList winners = findMinCost( finder.candidates );
    11661321                                if ( winners.size() != 1 ) {
    1167                                         SemanticError( 
     1322                                        SemanticError(
    11681323                                                alignofExpr->expr.get(), "Ambiguous expression in alignof operand: " );
    11691324                                }
     
    11721327                                choice->expr = referenceToRvalueConversion( choice->expr, choice->cost );
    11731328                                choice->cost = Cost::zero;
    1174                                 addCandidate( 
     1329                                addCandidate(
    11751330                                        *choice, new ast::AlignofExpr{ alignofExpr->location, choice->expr } );
    11761331                        }
     
    11781333
    11791334                void postvisit( const ast::UntypedOffsetofExpr * offsetofExpr ) {
    1180                         const ast::ReferenceToType * aggInst;
     1335                        const ast::BaseInstType * aggInst;
    11811336                        if (( aggInst = offsetofExpr->type.as< ast::StructInstType >() )) ;
    11821337                        else if (( aggInst = offsetofExpr->type.as< ast::UnionInstType >() )) ;
     
    11851340                        for ( const ast::Decl * member : aggInst->lookup( offsetofExpr->member ) ) {
    11861341                                auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( member );
    1187                                 addCandidate( 
     1342                                addCandidate(
    11881343                                        new ast::OffsetofExpr{ offsetofExpr->location, aggInst, dwt }, tenv );
    11891344                        }
     
    12061361                        finder2.find( logicalExpr->arg2, ResolvMode::withAdjustment() );
    12071362                        if ( finder2.candidates.empty() ) return;
     1363
     1364                        reason.code = NoMatch;
    12081365
    12091366                        for ( const CandidateRef & r1 : finder1.candidates ) {
     
    12181375
    12191376                                        addCandidate(
    1220                                                 new ast::LogicalExpr{ 
     1377                                                new ast::LogicalExpr{
    12211378                                                        logicalExpr->location, r1->expr, r2->expr, logicalExpr->isAnd },
    12221379                                                move( env ), move( open ), move( need ), r1->cost + r2->cost );
     
    12401397                        finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() );
    12411398                        if ( finder3.candidates.empty() ) return;
     1399
     1400                        reason.code = NoMatch;
    12421401
    12431402                        for ( const CandidateRef & r1 : finder1.candidates ) {
     
    12561415                                                ast::AssertionSet have;
    12571416
    1258                                                 // unify true and false results, then infer parameters to produce new 
     1417                                                // unify true and false results, then infer parameters to produce new
    12591418                                                // candidates
    12601419                                                ast::ptr< ast::Type > common;
    1261                                                 if ( 
    1262                                                         unify( 
    1263                                                                 r2->expr->result, r3->expr->result, env, need, have, open, symtab, 
    1264                                                                 common ) 
     1420                                                if (
     1421                                                        unify(
     1422                                                                r2->expr->result, r3->expr->result, env, need, have, open, symtab,
     1423                                                                common )
    12651424                                                ) {
    12661425                                                        // generate typed expression
    1267                                                         ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{ 
     1426                                                        ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{
    12681427                                                                conditionalExpr->location, r1->expr, r2->expr, r3->expr };
    12691428                                                        newExpr->result = common ? common : r2->expr->result;
    12701429                                                        // convert both options to result type
    12711430                                                        Cost cost = r1->cost + r2->cost + r3->cost;
    1272                                                         newExpr->arg2 = computeExpressionConversionCost( 
     1431                                                        newExpr->arg2 = computeExpressionConversionCost(
    12731432                                                                newExpr->arg2, newExpr->result, symtab, env, cost );
    12741433                                                        newExpr->arg3 = computeExpressionConversionCost(
     
    12871446                        ast::TypeEnvironment env{ tenv };
    12881447                        ast::ptr< ast::Expr > arg1 = resolveInVoidContext( commaExpr->arg1, symtab, env );
    1289                        
     1448
    12901449                        CandidateFinder finder2{ symtab, env };
    12911450                        finder2.find( commaExpr->arg2, ResolvMode::withAdjustment() );
     
    13171476                        finder2.find( rangeExpr->high, ResolvMode::withAdjustment() );
    13181477                        if ( finder2.candidates.empty() ) return;
     1478
     1479                        reason.code = NoMatch;
    13191480
    13201481                        for ( const CandidateRef & r1 : finder1.candidates ) {
     
    13301491
    13311492                                        ast::ptr< ast::Type > common;
    1332                                         if ( 
    1333                                                 unify( 
    1334                                                         r1->expr->result, r2->expr->result, env, need, have, open, symtab, 
    1335                                                         common ) 
     1493                                        if (
     1494                                                unify(
     1495                                                        r1->expr->result, r2->expr->result, env, need, have, open, symtab,
     1496                                                        common )
    13361497                                        ) {
    13371498                                                // generate new expression
    1338                                                 ast::RangeExpr * newExpr = 
     1499                                                ast::RangeExpr * newExpr =
    13391500                                                        new ast::RangeExpr{ rangeExpr->location, r1->expr, r2->expr };
    13401501                                                newExpr->result = common ? common : r1->expr->result;
    13411502                                                // add candidate
    13421503                                                CandidateRef newCand = std::make_shared<Candidate>(
    1343                                                         newExpr, move( env ), move( open ), move( need ), 
     1504                                                        newExpr, move( env ), move( open ), move( need ),
    13441505                                                        r1->cost + r2->cost );
    13451506                                                inferParameters( newCand, candidates );
     
    13501511
    13511512                void postvisit( const ast::UntypedTupleExpr * tupleExpr ) {
    1352                         std::vector< CandidateFinder > subCandidates = 
     1513                        std::vector< CandidateFinder > subCandidates =
    13531514                                selfFinder.findSubExprs( tupleExpr->exprs );
    13541515                        std::vector< CandidateList > possibilities;
     
    13701531
    13711532                                addCandidate(
    1372                                         new ast::TupleExpr{ tupleExpr->location, move( exprs ) }, 
     1533                                        new ast::TupleExpr{ tupleExpr->location, move( exprs ) },
    13731534                                        move( env ), move( open ), move( need ), sumCost( subs ) );
    13741535                        }
     
    14101571                                // calculate target type
    14111572                                const ast::Type * toType = resolveTypeof( initAlt.type, symtab );
    1412                                 toType = SymTab::validateType( initExpr->location, toType, symtab );
     1573                                // toType = SymTab::validateType( initExpr->location, toType, symtab );
    14131574                                toType = adjustExprType( toType, tenv, symtab );
    1414                                 // The call to find must occur inside this loop, otherwise polymorphic return 
    1415                                 // types are not bound to the initialization type, since return type variables are 
    1416                                 // only open for the duration of resolving the UntypedExpr. 
     1575                                // The call to find must occur inside this loop, otherwise polymorphic return
     1576                                // types are not bound to the initialization type, since return type variables are
     1577                                // only open for the duration of resolving the UntypedExpr.
    14171578                                CandidateFinder finder{ symtab, tenv, toType };
    14181579                                finder.find( initExpr->expr, ResolvMode::withAdjustment() );
    14191580                                for ( CandidateRef & cand : finder.candidates ) {
     1581                                        if(reason.code == NotFound) reason.code = NoMatch;
     1582
    14201583                                        ast::TypeEnvironment env{ cand->env };
    14211584                                        ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have;
     
    14261589                                        )
    14271590
    1428                                         // It is possible that a cast can throw away some values in a multiply-valued 
    1429                                         // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of 
    1430                                         // the subexpression results that are cast directly. The candidate is invalid 
     1591                                        // It is possible that a cast can throw away some values in a multiply-valued
     1592                                        // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of
     1593                                        // the subexpression results that are cast directly. The candidate is invalid
    14311594                                        // if it has fewer results than there are types to cast to.
    14321595                                        int discardedValues = cand->expr->result->size() - toType->size();
     
    14341597
    14351598                                        // unification run for side-effects
    1436                                         unify( toType, cand->expr->result, env, need, have, open, symtab );
    1437                                         Cost thisCost = castCost( cand->expr->result, toType, symtab, env );
    1438                                        
     1599                                        bool canUnify = unify( toType, cand->expr->result, env, need, have, open, symtab );
     1600                                        (void) canUnify;
     1601                                        Cost thisCost = computeConversionCost( cand->expr->result, toType, cand->expr->get_lvalue(),
     1602                                                symtab, env );
     1603                                        PRINT(
     1604                                                Cost legacyCost = castCost( cand->expr->result, toType, cand->expr->get_lvalue(),
     1605                                                        symtab, env );
     1606                                                std::cerr << "Considering initialization:";
     1607                                                std::cerr << std::endl << "  FROM: " << cand->expr->result << std::endl;
     1608                                                std::cerr << std::endl << "  TO: "   << toType             << std::endl;
     1609                                                std::cerr << std::endl << "  Unification " << (canUnify ? "succeeded" : "failed");
     1610                                                std::cerr << std::endl << "  Legacy cost " << legacyCost;
     1611                                                std::cerr << std::endl << "  New cost " << thisCost;
     1612                                                std::cerr << std::endl;
     1613                                        )
    14391614                                        if ( thisCost != Cost::infinity ) {
    14401615                                                // count one safe conversion for each value that is thrown away
    14411616                                                thisCost.incSafe( discardedValues );
    1442                                                 CandidateRef newCand = std::make_shared<Candidate>( 
    1443                                                         new ast::InitExpr{ 
    1444                                                                 initExpr->location, restructureCast( cand->expr, toType ), 
    1445                                                                 initAlt.designation }, 
    1446                                                         copy( cand->env ), move( open ), move( need ), cand->cost, thisCost );
     1617                                                CandidateRef newCand = std::make_shared<Candidate>(
     1618                                                        new ast::InitExpr{
     1619                                                                initExpr->location, restructureCast( cand->expr, toType ),
     1620                                                                initAlt.designation },
     1621                                                        move(env), move( open ), move( need ), cand->cost, thisCost );
    14471622                                                inferParameters( newCand, matches );
    14481623                                        }
    14491624                                }
     1625
    14501626                        }
    14511627
     
    14691645        };
    14701646
    1471         /// Prunes a list of candidates down to those that have the minimum conversion cost for a given
     1647        // size_t Finder::traceId = Stats::Heap::new_stacktrace_id("Finder");
     1648        /// Prunes a list of candidates down to those that have the minimum conversion cost for a given
    14721649        /// return type. Skips ambiguous candidates.
    1473         CandidateList pruneCandidates( CandidateList & candidates ) {
    1474                 struct PruneStruct {
    1475                         CandidateRef candidate;
    1476                         bool ambiguous;
    1477 
    1478                         PruneStruct() = default;
    1479                         PruneStruct( const CandidateRef & c ) : candidate( c ), ambiguous( false ) {}
    1480                 };
    1481 
    1482                 // find lowest-cost candidate for each type
    1483                 std::unordered_map< std::string, PruneStruct > selected;
    1484                 for ( CandidateRef & candidate : candidates ) {
    1485                         std::string mangleName;
     1650
     1651} // anonymous namespace
     1652
     1653bool CandidateFinder::pruneCandidates( CandidateList & candidates, CandidateList & out, std::vector<std::string> & errors ) {
     1654        struct PruneStruct {
     1655                CandidateRef candidate;
     1656                bool ambiguous;
     1657
     1658                PruneStruct() = default;
     1659                PruneStruct( const CandidateRef & c ) : candidate( c ), ambiguous( false ) {}
     1660        };
     1661
     1662        // find lowest-cost candidate for each type
     1663        std::unordered_map< std::string, PruneStruct > selected;
     1664        // attempt to skip satisfyAssertions on more expensive alternatives if better options have been found
     1665        std::sort(candidates.begin(), candidates.end(), [](const CandidateRef & x, const CandidateRef & y){return x->cost < y->cost;});
     1666        for ( CandidateRef & candidate : candidates ) {
     1667                std::string mangleName;
     1668                {
     1669                        ast::ptr< ast::Type > newType = candidate->expr->result;
     1670                        assertf(candidate->expr->result, "Result of expression %p for candidate is null", candidate->expr.get());
     1671                        candidate->env.apply( newType );
     1672                        mangleName = Mangle::mangle( newType );
     1673                }
     1674
     1675                auto found = selected.find( mangleName );
     1676                if (found != selected.end() && found->second.candidate->cost < candidate->cost) {
     1677                        PRINT(
     1678                                std::cerr << "cost " << candidate->cost << " loses to "
     1679                                        << found->second.candidate->cost << std::endl;
     1680                        )
     1681                        continue;
     1682                }
     1683
     1684                // xxx - when do satisfyAssertions produce more than 1 result?
     1685                // this should only happen when initial result type contains
     1686                // unbound type parameters, then it should never be pruned by
     1687                // the previous step, since renameTyVars guarantees the mangled name
     1688                // is unique.
     1689                CandidateList satisfied;
     1690                bool needRecomputeKey = false;
     1691                if (candidate->need.empty()) {
     1692                        satisfied.emplace_back(candidate);
     1693                }
     1694                else {
     1695                        satisfyAssertions(candidate, localSyms, satisfied, errors);
     1696                        needRecomputeKey = true;
     1697                }
     1698
     1699                for (auto & newCand : satisfied) {
     1700                        // recomputes type key, if satisfyAssertions changed it
     1701                        if (needRecomputeKey)
    14861702                        {
    1487                                 ast::ptr< ast::Type > newType = candidate->expr->result;
    1488                                 candidate->env.apply( newType );
     1703                                ast::ptr< ast::Type > newType = newCand->expr->result;
     1704                                assertf(newCand->expr->result, "Result of expression %p for candidate is null", newCand->expr.get());
     1705                                newCand->env.apply( newType );
    14891706                                mangleName = Mangle::mangle( newType );
    14901707                        }
    1491 
    14921708                        auto found = selected.find( mangleName );
    14931709                        if ( found != selected.end() ) {
    1494                                 if ( candidate->cost < found->second.candidate->cost ) {
     1710                                if ( newCand->cost < found->second.candidate->cost ) {
    14951711                                        PRINT(
    1496                                                 std::cerr << "cost " << candidate->cost << " beats "
     1712                                                std::cerr << "cost " << newCand->cost << " beats "
    14971713                                                        << found->second.candidate->cost << std::endl;
    14981714                                        )
    14991715
    1500                                         found->second = PruneStruct{ candidate };
    1501                                 } else if ( candidate->cost == found->second.candidate->cost ) {
    1502                                         // if one of the candidates contains a deleted identifier, can pick the other, 
    1503                                         // since deleted expressions should not be ambiguous if there is another option 
     1716                                        found->second = PruneStruct{ newCand };
     1717                                } else if ( newCand->cost == found->second.candidate->cost ) {
     1718                                        // if one of the candidates contains a deleted identifier, can pick the other,
     1719                                        // since deleted expressions should not be ambiguous if there is another option
    15041720                                        // that is at least as good
    1505                                         if ( findDeletedExpr( candidate->expr ) ) {
     1721                                        if ( findDeletedExpr( newCand->expr ) ) {
    15061722                                                // do nothing
    15071723                                                PRINT( std::cerr << "candidate is deleted" << std::endl; )
    15081724                                        } else if ( findDeletedExpr( found->second.candidate->expr ) ) {
    15091725                                                PRINT( std::cerr << "current is deleted" << std::endl; )
    1510                                                 found->second = PruneStruct{ candidate };
     1726                                                found->second = PruneStruct{ newCand };
    15111727                                        } else {
    15121728                                                PRINT( std::cerr << "marking ambiguous" << std::endl; )
    15131729                                                found->second.ambiguous = true;
    15141730                                        }
    1515                                 } else {
     1731                                } else {
     1732                                        // xxx - can satisfyAssertions increase the cost?
    15161733                                        PRINT(
    1517                                                 std::cerr << "cost " << candidate->cost << " loses to "
     1734                                                std::cerr << "cost " << newCand->cost << " loses to "
    15181735                                                        << found->second.candidate->cost << std::endl;
    1519                                         )
     1736                                        )       
    15201737                                }
    15211738                        } else {
    1522                                 selected.emplace_hint( found, mangleName, candidate );
    1523                         }
    1524                 }
    1525 
    1526                 // report unambiguous min-cost candidates
    1527                 CandidateList out;
    1528                 for ( auto & target : selected ) {
    1529                         if ( target.second.ambiguous ) continue;
    1530 
    1531                         CandidateRef cand = target.second.candidate;
    1532                        
    1533                         ast::ptr< ast::Type > newResult = cand->expr->result;
    1534                         cand->env.applyFree( newResult );
    1535                         cand->expr = ast::mutate_field(
    1536                                 cand->expr.get(), &ast::Expr::result, move( newResult ) );
    1537                        
    1538                         out.emplace_back( cand );
    1539                 }
    1540                 return out;
     1739                                selected.emplace_hint( found, mangleName, newCand );
     1740                        }
     1741                }
    15411742        }
    15421743
    1543 } // anonymous namespace
     1744        // report unambiguous min-cost candidates
     1745        // CandidateList out;
     1746        for ( auto & target : selected ) {
     1747                if ( target.second.ambiguous ) continue;
     1748
     1749                CandidateRef cand = target.second.candidate;
     1750
     1751                ast::ptr< ast::Type > newResult = cand->expr->result;
     1752                cand->env.applyFree( newResult );
     1753                cand->expr = ast::mutate_field(
     1754                        cand->expr.get(), &ast::Expr::result, move( newResult ) );
     1755
     1756                out.emplace_back( cand );
     1757        }
     1758        // if everything is lost in satisfyAssertions, report the error
     1759        return !selected.empty();
     1760}
    15441761
    15451762void CandidateFinder::find( const ast::Expr * expr, ResolvMode mode ) {
     
    15491766
    15501767        if ( mode.failFast && candidates.empty() ) {
    1551                 SemanticError( expr, "No reasonable alternatives for expression " );
     1768                switch(finder.core.reason.code) {
     1769                case Finder::NotFound:
     1770                        { SemanticError( expr, "No alternatives for expression " ); break; }
     1771                case Finder::NoMatch:
     1772                        { SemanticError( expr, "Invalid application of existing declaration(s) in expression " ); break; }
     1773                case Finder::ArgsToFew:
     1774                case Finder::ArgsToMany:
     1775                case Finder::RetsToFew:
     1776                case Finder::RetsToMany:
     1777                case Finder::NoReason:
     1778                default:
     1779                        { SemanticError( expr->location, "No reasonable alternatives for expression : reasons unkown" ); }
     1780                }
    15521781        }
    15531782
     1783        /*
    15541784        if ( mode.satisfyAssns || mode.prune ) {
    15551785                // trim candidates to just those where the assertions are satisfiable
     
    15581788                std::vector< std::string > errors;
    15591789                for ( CandidateRef & candidate : candidates ) {
    1560                         satisfyAssertions( candidate, symtab, satisfied, errors );
     1790                        satisfyAssertions( candidate, localSyms, satisfied, errors );
    15611791                }
    15621792
     
    15741804                candidates = move( satisfied );
    15751805        }
     1806        */
    15761807
    15771808        if ( mode.prune ) {
     
    15821813                )
    15831814
    1584                 CandidateList pruned = pruneCandidates( candidates );
    1585                
     1815                CandidateList pruned;
     1816                std::vector<std::string> errors;
     1817                bool found = pruneCandidates( candidates, pruned, errors );
     1818
    15861819                if ( mode.failFast && pruned.empty() ) {
    15871820                        std::ostringstream stream;
    1588                         CandidateList winners = findMinCost( candidates );
    1589                         stream << "Cannot choose between " << winners.size() << " alternatives for "
    1590                                 "expression\n";
    1591                         ast::print( stream, expr );
    1592                         stream << " Alternatives are:\n";
    1593                         print( stream, winners, 1 );
    1594                         SemanticError( expr->location, stream.str() );
     1821                        if (found) {           
     1822                                CandidateList winners = findMinCost( candidates );
     1823                                stream << "Cannot choose between " << winners.size() << " alternatives for "
     1824                                        "expression\n";
     1825                                ast::print( stream, expr );
     1826                                stream << " Alternatives are:\n";
     1827                                print( stream, winners, 1 );
     1828                                SemanticError( expr->location, stream.str() );
     1829                        }
     1830                        else {
     1831                                stream << "No alternatives with satisfiable assertions for " << expr << "\n";
     1832                                for ( const auto& err : errors ) {
     1833                                        stream << err;
     1834                                }
     1835                                SemanticError( expr->location, stream.str() );
     1836                        }
    15951837                }
    15961838
     
    16021844                )
    16031845                PRINT(
    1604                         std::cerr << "there are " << candidates.size() << " alternatives after elimination" 
     1846                        std::cerr << "there are " << candidates.size() << " alternatives after elimination"
    16051847                                << std::endl;
    16061848                )
    16071849        }
    16081850
    1609         // adjust types after pruning so that types substituted by pruneAlternatives are correctly 
     1851        // adjust types after pruning so that types substituted by pruneAlternatives are correctly
    16101852        // adjusted
    16111853        if ( mode.adjust ) {
    16121854                for ( CandidateRef & r : candidates ) {
    1613                         r->expr = ast::mutate_field( 
    1614                                 r->expr.get(), &ast::Expr::result, 
    1615                                 adjustExprType( r->expr->result, r->env, symtab ) );
     1855                        r->expr = ast::mutate_field(
     1856                                r->expr.get(), &ast::Expr::result,
     1857                                adjustExprType( r->expr->result, r->env, localSyms ) );
    16161858                }
    16171859        }
     
    16251867}
    16261868
    1627 std::vector< CandidateFinder > CandidateFinder::findSubExprs( 
    1628         const std::vector< ast::ptr< ast::Expr > > & xs 
     1869std::vector< CandidateFinder > CandidateFinder::findSubExprs(
     1870        const std::vector< ast::ptr< ast::Expr > > & xs
    16291871) {
    16301872        std::vector< CandidateFinder > out;
    16311873
    16321874        for ( const auto & x : xs ) {
    1633                 out.emplace_back( symtab, env );
     1875                out.emplace_back( localSyms, env );
    16341876                out.back().find( x, ResolvMode::withAdjustment() );
    1635                
     1877
    16361878                PRINT(
    16371879                        std::cerr << "findSubExprs" << std::endl;
  • src/ResolvExpr/CandidateFinder.hpp

    r3c64c668 r58fe85a  
    99// Author           : Aaron B. Moss
    1010// Created On       : Wed Jun 5 14:30:00 2019
    11 // Last Modified By : Aaron B. Moss
    12 // Last Modified On : Wed Jun 5 14:30:00 2019
    13 // Update Count     : 1
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Oct  1  9:51:00 2019
     13// Update Count     : 2
    1414//
    1515
     
    2828struct CandidateFinder {
    2929        CandidateList candidates;          ///< List of candidate resolutions
    30         const ast::SymbolTable & symtab;   ///< Symbol table to lookup candidates
     30        const ast::SymbolTable & localSyms;   ///< Symbol table to lookup candidates
    3131        const ast::TypeEnvironment & env;  ///< Substitutions performed in this resolution
    3232        ast::ptr< ast::Type > targetType;  ///< Target type for resolution
     33        std::set< std::string > otypeKeys;  /// different type may map to same key
    3334
    34         CandidateFinder( 
    35                 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
     35        CandidateFinder(
     36                const ast::SymbolTable & syms, const ast::TypeEnvironment & env,
    3637                const ast::Type * tt = nullptr )
    37         : candidates(), symtab( symtab ), env( env ), targetType( tt ) {}
     38        : candidates(), localSyms( syms ), env( env ), targetType( tt ) {}
    3839
    3940        /// Fill candidates with feasible resolutions for `expr`
    4041        void find( const ast::Expr * expr, ResolvMode mode = {} );
     42        bool pruneCandidates( CandidateList & candidates, CandidateList & out, std::vector<std::string> & errors );
    4143
    4244        /// Runs new candidate finder on each element in xs, returning the list of finders
     
    4951        iterator begin() { return candidates.begin(); }
    5052        const_iterator begin() const { return candidates.begin(); }
    51        
     53
    5254        iterator end() { return candidates.end(); }
    5355        const_iterator end() const { return candidates.end(); }
     
    5557
    5658/// Computes conversion cost between two types
    57 Cost computeConversionCost( 
    58         const ast::Type * argType, const ast::Type * paramType, const ast::SymbolTable & symtab,
    59         const ast::TypeEnvironment & env );
     59Cost computeConversionCost(
     60        const ast::Type * argType, const ast::Type * paramType, bool argIsLvalue,
     61        const ast::SymbolTable & symtab, const ast::TypeEnvironment & env );
    6062
    6163} // namespace ResolvExpr
  • src/ResolvExpr/CastCost.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Sun May 17 06:57:43 2015
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Thu Aug  8 16:12:00 2019
    13 // Update Count     : 8
     12// Last Modified On : Tue Oct  4 15:00:00 2019
     13// Update Count     : 9
    1414//
    1515
     
    142142
    143143                CastCost_new(
    144                         const ast::Type * dst, const ast::SymbolTable & symtab,
     144                        const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab,
    145145                        const ast::TypeEnvironment & env, CostCalculation costFunc )
    146                 : ConversionCost_new( dst, symtab, env, costFunc ) {}
     146                : ConversionCost_new( dst, srcIsLvalue, symtab, env, costFunc ) {}
    147147
    148148                void postvisit( const ast::BasicType * basicType ) {
     
    152152                                cost = Cost::unsafe;
    153153                        } else {
    154                                 cost = conversionCost( basicType, dst, symtab, env );
     154                                cost = conversionCost( basicType, dst, srcIsLvalue, symtab, env );
    155155                        }
    156156                }
     
    165165                                } else {
    166166                                        ast::TypeEnvironment newEnv{ env };
    167                                         if ( auto wParams = pointerType->base.as< ast::ParameterizedType >() ) {
     167                                        if ( auto wParams = pointerType->base.as< ast::FunctionType >() ) {
    168168                                                newEnv.add( wParams->forall );
    169169                                        }
     
    183183                }
    184184        };
     185
     186        #warning For overload resolution between the two versions.
     187        int localPtrsCastable(const ast::Type * t1, const ast::Type * t2,
     188                        const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) {
     189                return ptrsCastable( t1, t2, symtab, env );
     190        }
     191        Cost localCastCost(
     192                const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
     193                const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
     194        ) { return castCost( src, dst, srcIsLvalue, symtab, env ); }
    185195} // anonymous namespace
    186196
     197
     198
    187199Cost castCost(
    188         const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
    189         const ast::TypeEnvironment & env
     200        const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
     201        const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
    190202) {
    191203        if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    192                 if ( const ast::EqvClass * eqvClass = env.lookup( typeInst->name ) ) {
     204                if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst ) ) {
    193205                        // check cast cost against bound type, if present
    194206                        if ( eqvClass->bound ) {
    195                                 return castCost( src, eqvClass->bound, symtab, env );
     207                                return castCost( src, eqvClass->bound, srcIsLvalue, symtab, env );
    196208                        } else {
    197209                                return Cost::infinity;
     
    201213                        auto type = strict_dynamic_cast< const ast::TypeDecl * >( named );
    202214                        if ( type->base ) {
    203                                 return castCost( src, type->base, symtab, env ) + Cost::safe;
     215                                return castCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe;
    204216                        }
    205217                }
     
    224236                #warning cast on ptrsCastable artifact of having two functions, remove when port done
    225237                return convertToReferenceCost(
    226                         src, refType, symtab, env,
    227                         ( int (*)(
    228                                 const ast::Type *, const ast::Type *, const ast::SymbolTable &,
    229                                 const ast::TypeEnvironment & )
    230                         ) ptrsCastable );
     238                        src, refType, srcIsLvalue, symtab, env, localPtrsCastable );
    231239        } else {
    232240                #warning cast on castCost artifact of having two functions, remove when port done
    233                 ast::Pass< CastCost_new > converter{
    234                         dst, symtab, env,
    235                         ( Cost (*)(
    236                                 const ast::Type *, const ast::Type *, const ast::SymbolTable &,
    237                                 const ast::TypeEnvironment & )
    238                         ) castCost };
     241                ast::Pass< CastCost_new > converter(
     242                        dst, srcIsLvalue, symtab, env, localCastCost );
    239243                src->accept( converter );
    240                 return converter.pass.cost;
     244                return converter.core.cost;
    241245        }
    242246}
  • src/ResolvExpr/CommonType.cc

    r3c64c668 r58fe85a  
    666666                const ast::OpenVarSet & open;
    667667        public:
     668                static size_t traceId;
    668669                ast::ptr< ast::Type > result;
    669670
     
    712713                        const ast::Type * base = oPtr->base;
    713714                        if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) {
    714                                 auto entry = open.find( var->name );
     715                                auto entry = open.find( *var );
    715716                                if ( entry != open.end() ) {
    716717                                        ast::AssertionSet need, have;
     
    893894        };
    894895
     896        // size_t CommonType_new::traceId = Stats::Heap::new_stacktrace_id("CommonType_new");
    895897        namespace {
    896898                ast::ptr< ast::Type > handleReference(
     
    939941                        ast::ptr< ast::Type > result;
    940942                        const ast::ReferenceType * ref1 = type1.as< ast::ReferenceType >();
    941                         const ast::ReferenceType * ref2 = type1.as< ast::ReferenceType >();
     943                        const ast::ReferenceType * ref2 = type2.as< ast::ReferenceType >();
    942944
    943945                        if ( depth1 > depth2 ) {
     
    966968                ast::Pass<CommonType_new> visitor{ type2, widen, symtab, env, open };
    967969                type1->accept( visitor );
    968                 ast::ptr< ast::Type > result = visitor.pass.result;
     970                ast::ptr< ast::Type > result = visitor.core.result;
    969971
    970972                // handling for opaque type declarations (?)
  • src/ResolvExpr/ConversionCost.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Sun May 17 07:06:19 2015
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Aug 12 10:21:00 2019
    13 // Update Count     : 27
     12// Last Modified On : Wed Jul 29 16:11:00 2020
     13// Update Count     : 28
    1414//
    1515
     
    392392        void ConversionCost::postvisit( const FunctionType * ) {}
    393393
    394         void ConversionCost::postvisit( const StructInstType * inst ) {
    395                 if ( const StructInstType * destAsInst = dynamic_cast< const StructInstType * >( dest ) ) {
    396                         if ( inst->name == destAsInst->name ) {
    397                                 cost = Cost::zero;
    398                         } // if
    399                 } // if
    400         }
    401 
    402         void ConversionCost::postvisit( const UnionInstType * inst ) {
    403                 if ( const UnionInstType * destAsInst = dynamic_cast< const UnionInstType * >( dest ) ) {
    404                         if ( inst->name == destAsInst->name ) {
    405                                 cost = Cost::zero;
    406                         } // if
    407                 } // if
    408         }
    409 
    410394        void ConversionCost::postvisit( const EnumInstType * ) {
    411395                static Type::Qualifiers q;
     
    497481        }
    498482
    499 static int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2,
    500                 const ast::SymbolTable &, const ast::TypeEnvironment & env ) {
    501         return ptrsAssignable( t1, t2, env );
    502 }
    503 
    504 // TODO: This is used for overload resolution. It might be able to be dropped once the old system
    505 // is removed.
    506 static Cost localConversionCost(
    507         const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
    508         const ast::TypeEnvironment & env
    509 ) { return conversionCost( src, dst, symtab, env ); }
     483namespace {
     484        # warning For overload resolution between the two versions.
     485        int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2,
     486                        const ast::SymbolTable &, const ast::TypeEnvironment & env ) {
     487                return ptrsAssignable( t1, t2, env );
     488        }
     489        Cost localConversionCost(
     490                const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
     491                const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
     492        ) { return conversionCost( src, dst, srcIsLvalue, symtab, env ); }
     493}
    510494
    511495Cost conversionCost(
    512         const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
    513         const ast::TypeEnvironment & env
     496        const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
     497        const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
    514498) {
    515499        if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    516                 if ( const ast::EqvClass * eqv = env.lookup( inst->name ) ) {
     500                if ( const ast::EqvClass * eqv = env.lookup( *inst ) ) {
    517501                        if ( eqv->bound ) {
    518                                 return conversionCost(src, eqv->bound, symtab, env );
     502                                return conversionCost(src, eqv->bound, srcIsLvalue, symtab, env );
    519503                        } else {
    520504                                return Cost::infinity;
     
    524508                        assertf( type, "Unexpected typedef." );
    525509                        if ( type->base ) {
    526                                 return conversionCost( src, type->base, symtab, env ) + Cost::safe;
     510                                return conversionCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe;
    527511                        }
    528512                }
     
    534518        } else if ( const ast::ReferenceType * refType =
    535519                         dynamic_cast< const ast::ReferenceType * >( dst ) ) {
    536                 return convertToReferenceCost( src, refType, symtab, env, localPtrsAssignable );
     520                return convertToReferenceCost( src, refType, srcIsLvalue, symtab, env, localPtrsAssignable );
    537521        } else {
    538                 ast::Pass<ConversionCost_new> converter( dst, symtab, env, localConversionCost );
    539                 src->accept( converter );
    540                 return converter.pass.cost;
    541         }
    542 }
    543 
    544 static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst,
     522                return ast::Pass<ConversionCost_new>::read( src, dst, srcIsLvalue, symtab, env, localConversionCost );
     523        }
     524}
     525
     526static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
    545527                int diff, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
    546                 NumCostCalculation func ) {
     528                PtrsCalculation func ) {
    547529        if ( 0 < diff ) {
    548530                Cost cost = convertToReferenceCost(
    549                         strict_dynamic_cast< const ast::ReferenceType * >( src )->base,
    550                         dst, (diff - 1), symtab, env, func );
     531                        strict_dynamic_cast< const ast::ReferenceType * >( src )->base, dst,
     532                        srcIsLvalue, (diff - 1), symtab, env, func );
    551533                cost.incReference();
    552534                return cost;
     
    554536                Cost cost = convertToReferenceCost(
    555537                        src, strict_dynamic_cast< const ast::ReferenceType * >( dst )->base,
    556                         (diff + 1), symtab, env, func );
     538                        srcIsLvalue, (diff + 1), symtab, env, func );
    557539                cost.incReference();
    558540                return cost;
     
    579561                        }
    580562                } else {
    581                         ast::Pass<ConversionCost_new> converter( dst, symtab, env, localConversionCost );
    582                         src->accept( converter );
    583                         return converter.pass.cost;
     563                        return ast::Pass<ConversionCost_new>::read( src, dst, srcIsLvalue, symtab, env, localConversionCost );
    584564                }
    585565        } else {
     
    588568                assert( dstAsRef );
    589569                if ( typesCompatibleIgnoreQualifiers( src, dstAsRef->base, symtab, env ) ) {
    590                         if ( src->is_lvalue() ) {
     570                        if ( srcIsLvalue ) {
    591571                                if ( src->qualifiers == dstAsRef->base->qualifiers ) {
    592572                                        return Cost::reference;
     
    607587
    608588Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dst,
    609             const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
    610                 NumCostCalculation func ) {
     589                bool srcIsLvalue, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
     590                PtrsCalculation func ) {
    611591        int sdepth = src->referenceDepth(), ddepth = dst->referenceDepth();
    612         return convertToReferenceCost( src, dst, sdepth - ddepth, symtab, env, func );
     592        return convertToReferenceCost( src, dst, srcIsLvalue, sdepth - ddepth, symtab, env, func );
    613593}
    614594
     
    667647        assert( nullptr == dynamic_cast< const ast::ReferenceType * >( dst ) );
    668648
    669         cost = costCalc( refType->base, dst, symtab, env );
     649        cost = costCalc( refType->base, dst, srcIsLvalue, symtab, env );
    670650        if ( refType->base->qualifiers == dst->qualifiers ) {
    671651                cost.incReference();
     
    681661}
    682662
    683 void ConversionCost_new::postvisit( const ast::StructInstType * structInstType ) {
    684         if ( const ast::StructInstType * dstAsInst =
    685                         dynamic_cast< const ast::StructInstType * >( dst ) ) {
    686                 if ( structInstType->name == dstAsInst->name ) {
    687                         cost = Cost::zero;
    688                 }
    689         }
    690 }
    691 
    692 void ConversionCost_new::postvisit( const ast::UnionInstType * unionInstType ) {
    693         if ( const ast::UnionInstType * dstAsInst =
    694                         dynamic_cast< const ast::UnionInstType * >( dst ) ) {
    695                 if ( unionInstType->name == dstAsInst->name ) {
    696                         cost = Cost::zero;
    697                 }
    698         }
    699 }
    700 
    701663void ConversionCost_new::postvisit( const ast::EnumInstType * enumInstType ) {
    702664        (void)enumInstType;
    703         static const ast::BasicType integer( ast::BasicType::SignedInt );
    704         cost = costCalc( &integer, dst, symtab, env );
     665        static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicType::SignedInt ) };
     666        cost = costCalc( integer, dst, srcIsLvalue, symtab, env );
    705667        if ( cost < Cost::unsafe ) {
    706668                cost.incSafe();
     
    713675
    714676void ConversionCost_new::postvisit( const ast::TypeInstType * typeInstType ) {
    715         if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) {
    716                 cost = costCalc( eqv->bound, dst, symtab, env );
     677        if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) {
     678                cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env );
    717679        } else if ( const ast::TypeInstType * dstAsInst =
    718680                        dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    719                 if ( typeInstType->name == dstAsInst->name ) {
     681                if ( *typeInstType == *dstAsInst ) {
    720682                        cost = Cost::zero;
    721683                }
     
    724686                assertf( type, "Unexpected typedef.");
    725687                if ( type->base ) {
    726                         cost = costCalc( type->base, dst, symtab, env ) + Cost::safe;
     688                        cost = costCalc( type->base, dst, srcIsLvalue, symtab, env ) + Cost::safe;
    727689                }
    728690        }
     
    737699                auto dstEnd = dstAsTuple->types.end();
    738700                while ( srcIt != srcEnd && dstIt != dstEnd ) {
    739                         Cost newCost = costCalc( * srcIt++, * dstIt++, symtab, env );
     701                        Cost newCost = costCalc( * srcIt++, * dstIt++, srcIsLvalue, symtab, env );
    740702                        if ( newCost == Cost::infinity ) {
    741703                                return;
     
    772734                        cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
    773735                }
     736        } else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
     737                cost = Cost::zero;
     738                // +1 for zero_t ->, +1 for disambiguation
     739                cost.incSafe( maxIntCost + 2 );
    774740        }
    775741}
     
    789755                        cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
    790756                }
    791         } else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
    792                 cost = Cost::zero;
    793                 cost.incSafe( maxIntCost + 2 );
    794         }
    795 }
    796 
     757        }
     758}
     759// size_t ConversionCost_new::traceId = Stats::Heap::new_stacktrace_id("ConversionCost");
    797760
    798761} // namespace ResolvExpr
  • src/ResolvExpr/ConversionCost.h

    r3c64c668 r58fe85a  
    1010// Created On       : Sun May 17 09:37:28 2015
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Thu Aug  8 16:13:00 2019
    13 // Update Count     : 6
     12// Last Modified On : Wed Jul 29 16:12:00 2020
     13// Update Count     : 7
    1414//
    1515
     
    5151                void postvisit( const ReferenceType * refType );
    5252                void postvisit( const FunctionType * functionType );
    53                 void postvisit( const StructInstType * aggregateUseType );
    54                 void postvisit( const UnionInstType * aggregateUseType );
    5553                void postvisit( const EnumInstType * aggregateUseType );
    5654                void postvisit( const TraitInstType * aggregateUseType );
     
    7472
    7573// Some function pointer types, differ in return type.
    76 using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *,
     74using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *, bool,
    7775        const ast::SymbolTable &, const ast::TypeEnvironment &)>;
    78 using NumCostCalculation = std::function<int(const ast::Type *, const ast::Type *,
     76using PtrsCalculation = std::function<int(const ast::Type *, const ast::Type *,
    7977        const ast::SymbolTable &, const ast::TypeEnvironment &)>;
    8078
     
    8381protected:
    8482        const ast::Type * dst;
     83        bool srcIsLvalue;
    8584        const ast::SymbolTable & symtab;
    8685        const ast::TypeEnvironment & env;
    8786        CostCalculation costCalc;
    8887public:
     88        static size_t traceId;
    8989        Cost cost;
     90        Cost result() { return cost; }
    9091
    91         ConversionCost_new( const ast::Type * dst, const ast::SymbolTable & symtab,
     92        ConversionCost_new( const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab,
    9293                        const ast::TypeEnvironment & env, CostCalculation costCalc ) :
    93                 dst( dst ), symtab( symtab ), env( env ), costCalc( costCalc ), cost( Cost::infinity )
     94                dst( dst ), srcIsLvalue( srcIsLvalue ), symtab( symtab ), env( env ),
     95                costCalc( costCalc ), cost( Cost::infinity )
    9496        {}
    9597
     
    102104        void postvisit( const ast::ReferenceType * refType );
    103105        void postvisit( const ast::FunctionType * functionType );
    104         void postvisit( const ast::StructInstType * structInstType );
    105         void postvisit( const ast::UnionInstType * unionInstType );
    106106        void postvisit( const ast::EnumInstType * enumInstType );
    107107        void postvisit( const ast::TraitInstType * traitInstType );
     
    114114
    115115Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dest,
    116         const ast::SymbolTable & indexer, const ast::TypeEnvironment & env, NumCostCalculation func );
     116        bool srcIsLvalue, const ast::SymbolTable & indexer, const ast::TypeEnvironment & env,
     117        PtrsCalculation func );
    117118
    118119} // namespace ResolvExpr
  • src/ResolvExpr/CurrentObject.cc

    r3c64c668 r58fe85a  
    2121#include <string>                      // for string, operator<<, allocator
    2222
     23#include "AST/Copy.hpp"                // for shallowCopy
    2324#include "AST/Expr.hpp"                // for InitAlternative
    2425#include "AST/GenericSubstitution.hpp" // for genericSubstitution
    2526#include "AST/Init.hpp"                // for Designation
    2627#include "AST/Node.hpp"                // for readonly
     28#include "AST/Print.hpp"                // for readonly
    2729#include "AST/Type.hpp"
    2830#include "Common/Indenter.h"           // for Indenter, operator<<
     
    592594        class SimpleIterator final : public MemberIterator {
    593595                CodeLocation location;
    594                 readonly< Type > type = nullptr;
     596                const Type * type = nullptr;
    595597        public:
    596598                SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {}
    597599
    598                 void setPosition( 
    599                         std::deque< ptr< Expr > >::const_iterator begin, 
     600                void setPosition(
     601                        std::deque< ptr< Expr > >::const_iterator begin,
    600602                        std::deque< ptr< Expr > >::const_iterator end
    601603                ) override {
     
    628630        class ArrayIterator final : public MemberIterator {
    629631                CodeLocation location;
    630                 readonly< ArrayType > array = nullptr;
    631                 readonly< Type > base = nullptr;
     632                const ArrayType * array = nullptr;
     633                const Type * base = nullptr;
    632634                size_t index = 0;
    633635                size_t size = 0;
     
    637639                        auto res = eval(expr);
    638640                        if ( ! res.second ) {
    639                                 SemanticError( location, 
     641                                SemanticError( location,
    640642                                        toString("Array designator must be a constant expression: ", expr ) );
    641643                        }
     
    644646
    645647        public:
    646                 ArrayIterator( const CodeLocation & loc, const ArrayType * at ) 
     648                ArrayIterator( const CodeLocation & loc, const ArrayType * at )
    647649                : location( loc ), array( at ), base( at->base ) {
    648650                        PRINT( std::cerr << "Creating array iterator: " << at << std::endl; )
     
    655657
    656658                void setPosition( const Expr * expr ) {
    657                         // need to permit integer-constant-expressions, including: integer constants, 
    658                         // enumeration constants, character constants, sizeof expressions, alignof expressions, 
     659                        // need to permit integer-constant-expressions, including: integer constants,
     660                        // enumeration constants, character constants, sizeof expressions, alignof expressions,
    659661                        // cast expressions
    660662                        if ( auto constExpr = dynamic_cast< const ConstantExpr * >( expr ) ) {
     
    662664                                        index = constExpr->intValue();
    663665                                } catch ( SemanticErrorException & ) {
    664                                         SemanticError( expr, 
     666                                        SemanticError( expr,
    665667                                                "Constant expression of non-integral type in array designator: " );
    666668                                }
    667669                        } else if ( auto castExpr = dynamic_cast< const CastExpr * >( expr ) ) {
    668670                                setPosition( castExpr->arg );
    669                         } else if ( 
    670                                 dynamic_cast< const SizeofExpr * >( expr ) 
    671                                 || dynamic_cast< const AlignofExpr * >( expr ) 
     671                        } else if (
     672                                dynamic_cast< const SizeofExpr * >( expr )
     673                                || dynamic_cast< const AlignofExpr * >( expr )
    672674                        ) {
    673675                                index = 0;
    674676                        } else {
    675                                 assertf( false, 
     677                                assertf( false,
    676678                                        "bad designator given to ArrayIterator: %s", toString( expr ).c_str() );
    677679                        }
    678680                }
    679681
    680                 void setPosition( 
    681                         std::deque< ptr< Expr > >::const_iterator begin, 
     682                void setPosition(
     683                        std::deque< ptr< Expr > >::const_iterator begin,
    682684                        std::deque< ptr< Expr > >::const_iterator end
    683685                ) override {
     
    758760                }
    759761
    760                 AggregateIterator( 
    761                         const CodeLocation & loc, const std::string k, const std::string & n, const Type * i, 
     762                AggregateIterator(
     763                        const CodeLocation & loc, const std::string k, const std::string & n, const Type * i,
    762764                        const MemberList & ms )
    763                 : location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ), 
     765                : location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ),
    764766                  sub( genericSubstitution( i ) ) {
    765767                        PRINT( std::cerr << "Creating " << kind << "(" << name << ")"; )
     
    768770
    769771        public:
    770                 void setPosition( 
    771                         std::deque< ptr< Expr > >::const_iterator begin, 
     772                void setPosition(
     773                        std::deque< ptr< Expr > >::const_iterator begin,
    772774                        std::deque< ptr< Expr > >::const_iterator end
    773775                ) final {
     
    786788                                        return;
    787789                                }
    788                                 assertf( false, 
     790                                assertf( false,
    789791                                        "could not find member in %s: %s", kind.c_str(), toString( varExpr ).c_str() );
    790792                        } else {
    791                                 assertf( false, 
     793                                assertf( false,
    792794                                        "bad designator given to %s: %s", kind.c_str(), toString( *begin ).c_str() );
    793795                        }
     
    803805                                                new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
    804806                                        // need to substitute for generic types so that casts are to concrete types
     807                                        alt.type = shallowCopy(alt.type.get());
    805808                                        PRINT( std::cerr << "  type is: " << alt.type; )
    806809                                        sub.apply( alt.type ); // also apply to designation??
     
    842845                                for ( InitAlternative & alt : ret ) {
    843846                                        PRINT( std::cerr << "iterating and adding designators" << std::endl; )
    844                                         alt.designation.get_and_mutate()->designators.emplace_front( 
     847                                        alt.designation.get_and_mutate()->designators.emplace_front(
    845848                                                new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
    846849                                }
     
    897900        class TupleIterator final : public AggregateIterator {
    898901        public:
    899                 TupleIterator( const CodeLocation & loc, const TupleType * inst ) 
    900                 : AggregateIterator( 
    901                         loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members 
     902                TupleIterator( const CodeLocation & loc, const TupleType * inst )
     903                : AggregateIterator(
     904                        loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members
    902905                ) {}
    903906
     
    920923
    921924        MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type ) {
    922                 if ( auto aggr = dynamic_cast< const ReferenceToType * >( type ) ) {
     925                if ( auto aggr = dynamic_cast< const BaseInstType * >( type ) ) {
    923926                        if ( auto sit = dynamic_cast< const StructInstType * >( aggr ) ) {
    924927                                return new StructIterator{ loc, sit };
     
    926929                                return new UnionIterator{ loc, uit };
    927930                        } else {
    928                                 assertf( 
    929                                         dynamic_cast< const EnumInstType * >( aggr )
    930                                                 || dynamic_cast< const TypeInstType * >( aggr ),
    931                                         "Encountered unhandled ReferenceToType in createMemberIterator: %s",
     931                                assertf(
     932                                        dynamic_cast< const EnumInstType * >( type )
     933                                                || dynamic_cast< const TypeInstType * >( type ),
     934                                        "Encountered unhandled BaseInstType in createMemberIterator: %s",
    932935                                                toString( type ).c_str() );
    933936                                return new SimpleIterator{ loc, type };
     
    949952                using DesignatorChain = std::deque< ptr< Expr > >;
    950953                PRINT( std::cerr << "___findNext" << std::endl; )
    951                
     954
    952955                // find all the d's
    953956                std::vector< DesignatorChain > desigAlts{ {} }, newDesigAlts;
     
    962965                                        DesignatorChain & d = *dit;
    963966                                        PRINT( std::cerr << "____actual: " << t << std::endl; )
    964                                         if ( auto refType = dynamic_cast< const ReferenceToType * >( t ) ) {
     967                                        if ( auto refType = dynamic_cast< const BaseInstType * >( t ) ) {
    965968                                                // concatenate identical field names
    966969                                                for ( const Decl * mem : refType->lookup( nexpr->name ) ) {
     
    10131016                // set new designators
    10141017                assertf( ! objStack.empty(), "empty object stack when setting designation" );
    1015                 Designation * actualDesignation = 
     1018                Designation * actualDesignation =
    10161019                        new Designation{ designation->location, DesignatorChain{d} };
    10171020                objStack.back()->setPosition( d ); // destroys d
  • src/ResolvExpr/FindOpenVars.cc

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

    r3c64c668 r58fe85a  
    5858
    5959// TODO: When the old PolyCost is torn out get rid of the _new suffix.
    60 struct PolyCost_new {
     60class PolyCost_new {
     61        const ast::SymbolTable &symtab;
     62public:
    6163        int result;
    62         const ast::SymbolTable &symtab;
    6364        const ast::TypeEnvironment &env_;
    6465
    65         PolyCost_new( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) :
    66                 result( 0 ), symtab( symtab ), env_( env ) {}
     66        PolyCost_new( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env )
     67        : symtab( symtab ), result( 0 ), env_( env ) {}
    6768
    6869        void previsit( const ast::TypeInstType * type ) {
    69                 if ( const ast::EqvClass * eqv = env_.lookup( type->name ) ) /* && */ if ( eqv->bound ) {
     70                if ( const ast::EqvClass * eqv = env_.lookup( *type ) ) /* && */ if ( eqv->bound ) {
    7071                        if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) {
    7172                                if ( symtab.lookupType( otherType->name ) ) {
     
    8687        ast::Pass<PolyCost_new> costing( symtab, env );
    8788        type->accept( costing );
    88         return costing.pass.result;
     89        return costing.core.result;
    8990}
    9091
  • src/ResolvExpr/PtrsAssignable.cc

    r3c64c668 r58fe85a  
    134134        }
    135135        void postvisit( const ast::TypeInstType * inst ) {
    136                 if ( const ast::EqvClass * eqv = typeEnv.lookup( inst->name ) ) {
     136                if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst ) ) {
    137137                        if ( eqv->bound ) {
    138138                                // T * = S * for any S depends on the type bound to T
     
    146146                const ast::TypeEnvironment & env ) {
    147147        if ( const ast::TypeInstType * dstAsInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    148                 if ( const ast::EqvClass * eqv = env.lookup( dstAsInst->name ) ) {
     148                if ( const ast::EqvClass * eqv = env.lookup( *dstAsInst ) ) {
    149149                        return ptrsAssignable( src, eqv->bound, env );
    150150                }
     
    155155                ast::Pass<PtrsAssignable_new> visitor( dst, env );
    156156                src->accept( visitor );
    157                 return visitor.pass.result;
     157                return visitor.core.result;
    158158        }
    159159
  • src/ResolvExpr/PtrsCastable.cc

    r3c64c668 r58fe85a  
    180180                                        }
    181181                                }
    182                         } else if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) {
     182                        } else if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
    183183                                if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
    184184                                        return -1;
     
    283283) {
    284284        if ( auto inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
    285                 if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) {
     285                if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
    286286                        return ptrsAssignable( src, eqvClass->bound, env );
    287287                }
     
    293293                ast::Pass< PtrsCastable_new > ptrs{ dst, env, symtab };
    294294                src->accept( ptrs );
    295                 return ptrs.pass.result;
     295                return ptrs.core.result;
    296296        }
    297297}
  • src/ResolvExpr/RenameVars.cc

    r3c64c668 r58fe85a  
    3030#include "SynTree/Visitor.h"       // for acceptAll, maybeAccept
    3131
     32#include "AST/Copy.hpp"
     33
    3234namespace ResolvExpr {
    3335
     
    3638                int level = 0;
    3739                int resetCount = 0;
     40
     41                int next_expr_id = 1;
     42                int next_usage_id = 1;
    3843                ScopedMap< std::string, std::string > nameMap;
    39 
     44                ScopedMap< std::string, ast::TypeInstType::TypeEnvKey > idMap;
    4045        public:
    4146                void reset() {
     
    4449                }
    4550
    46                 using mapConstIterator = ScopedMap< std::string, std::string >::const_iterator;
    47 
    4851                void rename( TypeInstType * type ) {
    49                         mapConstIterator it = nameMap.find( type->name );
     52                        auto it = nameMap.find( type->name );
    5053                        if ( it != nameMap.end() ) {
    5154                                type->name = it->second;
    5255                        }
     56                }
     57
     58                void nextUsage() {
     59                        ++next_usage_id;
    5360                }
    5461
     
    6572                                        // ditto for assertion names, the next level in
    6673                                        level++;
    67                                         // acceptAll( td->assertions, *this );
    68                                 } // for
    69                         } // if
     74                                }
     75                        }
    7076                }
    7177
     
    7783
    7884                const ast::TypeInstType * rename( const ast::TypeInstType * type ) {
    79                         mapConstIterator it = nameMap.find( type->name );
    80                         if ( it != nameMap.end() ) {
    81                                 ast::TypeInstType * mutType = ast::mutate( type );
    82                                 mutType->name = it->second;
    83                     type = mutType;
    84                         }
     85                        // rename
     86                        auto it = idMap.find( type->name );
     87                        if ( it != idMap.end() ) {
     88                                // unconditionally mutate because map will *always* have different name
     89                                ast::TypeInstType * mut = ast::shallowCopy( type );
     90                                // reconcile base node since some copies might have been made
     91                                mut->base = it->second.base;
     92                                mut->formal_usage = it->second.formal_usage;
     93                                mut->expr_id = it->second.expr_id;
     94                    type = mut;
     95                        }
     96
    8597                        return type;
    8698                }
    8799
    88                 template<typename NodeT>
    89                 const NodeT * openLevel( const NodeT * type ) {
    90                         if ( !type->forall.empty() ) {
    91                                 nameMap.beginScope();
    92                                 // Load new names from this forall clause and perform renaming.
    93                                 NodeT * mutType = ast::mutate( type );
    94                                 for ( ast::ptr< ast::TypeDecl > & td : mutType->forall ) {
    95                                         std::ostringstream output;
    96                                         output << "_" << resetCount << "_" << level << "_" << td->name;
    97                                         std::string newname( output.str() );
    98                                         nameMap[ td->name ] = newname;
    99                                         ++level;
    100 
    101                                         ast::TypeDecl * decl = ast::mutate( td.get() );
    102                                         decl->name = newname;
    103                                         td = decl;
    104                                 }
    105                         }
    106                         return type;
    107                 }
    108 
    109                 template<typename NodeT>
    110                 const NodeT * closeLevel( const NodeT * type ) {
    111                         if ( !type->forall.empty() ) {
    112                                 nameMap.endScope();
    113                         }
    114                         return type;
     100                const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) {
     101                        if ( type->forall.empty() ) return type;
     102                        idMap.beginScope();
     103
     104                        // Load new names from this forall clause and perform renaming.
     105                        auto mutType = ast::shallowCopy( type );
     106                        // assert( type == mutType && "mutated type must be unique from ForallSubstitutor" );
     107                        for ( auto & td : mutType->forall ) {
     108                                auto mut = ast::shallowCopy( td.get() );
     109                                // assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" );
     110
     111                                if (mode == GEN_EXPR_ID) {
     112                                        mut->expr_id = next_expr_id;
     113                                        mut->formal_usage = -1;
     114                                        ++next_expr_id;
     115                                }
     116                                else if (mode == GEN_USAGE) {
     117                                        assertf(mut->expr_id, "unfilled expression id in generating candidate type");
     118                                        mut->formal_usage = next_usage_id;
     119                                }
     120                                else {
     121                                        assert(false);
     122                                }
     123                                idMap[ td->name ] = ast::TypeInstType::TypeEnvKey(*mut);
     124                               
     125                                td = mut;
     126                        }
     127
     128                        return mutType;
     129                }
     130
     131                void closeLevel( const ast::FunctionType * type ) {
     132                        if ( type->forall.empty() ) return;
     133                        idMap.endScope();
    115134                }
    116135        };
     
    119138        RenamingData renaming;
    120139
    121         struct RenameVars {
     140        struct RenameVars_old {
    122141                void previsit( TypeInstType * instType ) {
    123142                        renaming.openLevel( (Type*)instType );
     
    130149                        renaming.closeLevel( type );
    131150                }
     151        };
     152
     153        struct RenameVars_new : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ {
     154                RenameMode mode;
    132155
    133156                const ast::FunctionType * previsit( const ast::FunctionType * type ) {
    134                         return renaming.openLevel( type );
    135                 }
     157                        return renaming.openLevel( type, mode );
     158                }
     159
     160                /*
    136161                const ast::StructInstType * previsit( const ast::StructInstType * type ) {
    137162                        return renaming.openLevel( type );
     
    143168                        return renaming.openLevel( type );
    144169                }
     170                */
     171
    145172                const ast::TypeInstType * previsit( const ast::TypeInstType * type ) {
    146                         return renaming.rename( renaming.openLevel( type ) );
    147                 }
    148                 const ast::ParameterizedType * postvisit( const ast::ParameterizedType * type ) {
    149                         return renaming.closeLevel( type );
     173                        if (mode == GEN_USAGE && !type->formal_usage) return type; // do not rename an actual type
     174                        return renaming.rename( type );
     175                }
     176                void postvisit( const ast::FunctionType * type ) {
     177                        renaming.closeLevel( type );
    150178                }
    151179        };
     
    154182
    155183void renameTyVars( Type * t ) {
    156         PassVisitor<RenameVars> renamer;
     184        PassVisitor<RenameVars_old> renamer;
    157185        t->accept( renamer );
    158186}
    159187
    160 const ast::Type * renameTyVars( const ast::Type * t ) {
    161         ast::Pass<RenameVars> renamer;
     188const ast::Type * renameTyVars( const ast::Type * t, RenameMode mode, bool reset ) {
     189        // ast::Type *tc = ast::deepCopy(t);
     190        ast::Pass<RenameVars_new> renamer;
     191        renamer.core.mode = mode;
     192        if (mode == GEN_USAGE && reset) {
     193                renaming.nextUsage();
     194        }
    162195        return t->accept( renamer );
    163196}
     
    165198void resetTyVarRenaming() {
    166199        renaming.reset();
     200        renaming.nextUsage();
    167201}
    168202
  • src/ResolvExpr/RenameVars.h

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

    r3c64c668 r58fe85a  
    2222                const bool prune;            ///< Prune alternatives to min-cost per return type? [true]
    2323                const bool failFast;         ///< Fail on no resulting alternatives? [true]
    24                 const bool satisfyAssns;     ///< Satisfy assertions? [false]
    2524
    26         private:
    27                 constexpr ResolvMode(bool a, bool p, bool ff, bool sa)
    28                 : adjust(a), prune(p), failFast(ff), satisfyAssns(sa) {}
     25                constexpr ResolvMode(bool a, bool p, bool ff)
     26                : adjust(a), prune(p), failFast(ff) {}
    2927
    30         public:
    3128                /// Default settings
    32                 constexpr ResolvMode() : adjust(false), prune(true), failFast(true), satisfyAssns(false) {}
     29                constexpr ResolvMode() : adjust(false), prune(true), failFast(true) {}
    3330               
    3431                /// With adjust flag set; turns array and function types into equivalent pointers
    35                 static constexpr ResolvMode withAdjustment() { return { true, true, true, false }; }
     32                static constexpr ResolvMode withAdjustment() { return { true, true, true }; }
    3633
    3734                /// With adjust flag set but prune unset; pruning ensures there is at least one alternative
    3835                /// per result type
    39                 static constexpr ResolvMode withoutPrune() { return { true, false, true, false }; }
     36                static constexpr ResolvMode withoutPrune() { return { true, false, true }; }
    4037
    4138                /// With adjust and prune flags set but failFast unset; failFast ensures there is at least
    4239                /// one resulting alternative
    43                 static constexpr ResolvMode withoutFailFast() { return { true, true, false, false }; }
     40                static constexpr ResolvMode withoutFailFast() { return { true, true, false }; }
    4441
    4542                /// The same mode, but with satisfyAssns turned on; for top-level calls
    46                 ResolvMode atTopLevel() const { return { adjust, prune, failFast, true }; }
     43                ResolvMode atTopLevel() const { return { adjust, true, failFast }; }
    4744        };
    4845} // namespace ResolvExpr
  • src/ResolvExpr/ResolveAssertions.cc

    r3c64c668 r58fe85a  
    277277                        const DeclarationWithType * candidate = cdata.id;
    278278
    279                         // build independent unification context for candidate
     279                        // ignore deleted candidates.
     280                        // NOTE: this behavior is different from main resolver.
     281                        // further investigations might be needed to determine
     282                        // if we should implement the same rule here
     283                        // (i.e. error if unique best match is deleted)
     284                        if (candidate->isDeleted) continue;
     285
     286                        // build independent unification context. for candidate
    280287                        AssertionSet have, newNeed;
    281288                        TypeEnvironment newEnv{ resn.alt.env };
     
    390397
    391398        /// Limit to depth of recursion of assertion satisfaction
    392         static const int recursionLimit = 4;
     399        static const int recursionLimit = 7;
    393400        /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of
    394401        static const int deferLimit = 10;
  • src/ResolvExpr/ResolveTypeof.cc

    r3c64c668 r58fe85a  
    1515
    1616#include "ResolveTypeof.h"
     17#include "RenameVars.h"
    1718
    1819#include <cassert>               // for assert
     
    2930#include "SynTree/Mutator.h"     // for Mutator
    3031#include "SynTree/Type.h"        // for TypeofType, Type
     32#include "SymTab/Mangler.h"
     33#include "InitTweak/InitTweak.h" // for isConstExpr
    3134
    3235namespace SymTab {
     
    99102                        // replace basetypeof(<enum>) by int
    100103                        if ( dynamic_cast<EnumInstType*>(newType) ) {
    101                                 Type* newerType = 
    102                                         new BasicType{ newType->get_qualifiers(), BasicType::SignedInt, 
     104                                Type* newerType =
     105                                        new BasicType{ newType->get_qualifiers(), BasicType::SignedInt,
    103106                                        newType->attributes };
    104107                                delete newType;
    105108                                newType = newerType;
    106109                        }
    107                         newType->get_qualifiers().val 
     110                        newType->get_qualifiers().val
    108111                                = ( newType->get_qualifiers().val & ~Type::Qualifiers::Mask ) | oldQuals;
    109112                } else {
    110113                        newType->get_qualifiers().val |= oldQuals;
    111114                }
    112                
     115
    113116                return newType;
    114117        }
     
    120123                ResolveTypeof_new( const ast::SymbolTable & syms ) : localSymtab( syms ) {}
    121124
    122                 void premutate( const ast::TypeofType * ) { visit_children = false; }
    123 
    124                 const ast::Type * postmutate( const ast::TypeofType * typeofType ) {
     125                void previsit( const ast::TypeofType * ) { visit_children = false; }
     126
     127                const ast::Type * postvisit( const ast::TypeofType * typeofType ) {
    125128                        // pass on null expression
    126129                        if ( ! typeofType->expr ) return typeofType;
     
    133136                                // typeof wrapping expression
    134137                                ast::TypeEnvironment dummy;
    135                                 ast::ptr< ast::Expr > newExpr = 
     138                                ast::ptr< ast::Expr > newExpr =
    136139                                        resolveInVoidContext( typeofType->expr, localSymtab, dummy );
    137140                                assert( newExpr->result && ! newExpr->result->isVoid() );
     
    143146                                // replace basetypeof(<enum>) by int
    144147                                if ( newType.as< ast::EnumInstType >() ) {
    145                                         newType = new ast::BasicType{ 
     148                                        newType = new ast::BasicType{
    146149                                                ast::BasicType::SignedInt, newType->qualifiers, copy(newType->attributes) };
    147150                                }
    148                                 reset_qualifiers( 
    149                                         newType, 
     151                                reset_qualifiers(
     152                                        newType,
    150153                                        ( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers );
    151154                        } else {
     
    153156                        }
    154157
    155                         return newType;
     158                        return newType.release();
    156159                }
    157160        };
     
    161164        ast::Pass< ResolveTypeof_new > mutator{ symtab };
    162165        return type->accept( mutator );
     166}
     167
     168struct FixArrayDimension {
     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) {}
     172
     173        const ast::ArrayType * previsit (const ast::ArrayType * arrayType) {
     174                if (!arrayType->dimension) return arrayType;
     175                auto mutType = mutate(arrayType);
     176                ast::ptr<ast::Type> sizetype = ast::sizeType ? ast::sizeType : new ast::BasicType(ast::BasicType::LongUnsignedInt);
     177                mutType->dimension = findSingleExpression(arrayType->dimension, sizetype, _symtab);
     178
     179                if (InitTweak::isConstExpr(mutType->dimension)) {
     180                        mutType->isVarLen = ast::LengthFlag::FixedLen;
     181                }
     182                else {
     183                        mutType->isVarLen = ast::LengthFlag::VariableLen;
     184                }
     185                return mutType;
     186        }
     187};
     188
     189const ast::Type * fixArrayType( const ast::Type * type, const ast::SymbolTable & symtab) {
     190        ast::Pass<FixArrayDimension> visitor {symtab};
     191        return type->accept(visitor);
     192}
     193
     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);
     199                mutDecl->type = resolvedType;
     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;
    163226}
    164227
  • src/ResolvExpr/ResolveTypeof.h

    r3c64c668 r58fe85a  
    2323        class Type;
    2424        class SymbolTable;
     25        class ObjectDecl;
    2526}
    2627
     
    2829        Type *resolveTypeof( Type*, const SymTab::Indexer &indexer );
    2930        const ast::Type * resolveTypeof( const ast::Type *, const ast::SymbolTable & );
     31        const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ast::SymbolTable & symtab );
    3032} // namespace ResolvExpr
    3133
  • src/ResolvExpr/Resolver.cc

    r3c64c668 r58fe85a  
    99// Author           : Aaron B. Moss
    1010// Created On       : Sun May 17 12:17:01 2015
    11 // Last Modified By : Aaron B. Moss
    12 // Last Modified On : Wed May 29 11:00:00 2019
    13 // Update Count     : 241
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Fri Mar 27 11:58:00 2020
     13// Update Count     : 242
    1414//
    1515
     
    2626#include "RenameVars.h"                  // for RenameVars, global_renamer
    2727#include "Resolver.h"
     28#include "ResolveTypeof.h"
    2829#include "ResolvMode.h"                  // for ResolvMode
    2930#include "typeops.h"                     // for extractResultType
    3031#include "Unify.h"                       // for unify
     32#include "CompilationState.h"
    3133#include "AST/Chain.hpp"
    3234#include "AST/Decl.hpp"
     
    3840#include "Common/PassVisitor.h"          // for PassVisitor
    3941#include "Common/SemanticError.h"        // for SemanticError
     42#include "Common/Stats/ResolveTime.h"    // for ResolveTime::start(), ResolveTime::stop()
    4043#include "Common/utility.h"              // for ValueGuard, group_iterate
    4144#include "InitTweak/GenInit.h"
     
    4447#include "SymTab/Autogen.h"              // for SizeType
    4548#include "SymTab/Indexer.h"              // for Indexer
     49#include "SymTab/Mangler.h"              // for Mangler
    4650#include "SynTree/Declaration.h"         // for ObjectDecl, TypeDecl, Declar...
    4751#include "SynTree/Expression.h"          // for Expression, CastExpr, InitExpr
     
    560564                // TODO: Replace *exception type with &exception type.
    561565                if ( throwStmt->get_expr() ) {
    562                         const StructDecl * exception_decl = indexer.lookupStruct( "__cfaabi_ehm__base_exception_t" );
     566                        const StructDecl * exception_decl = indexer.lookupStruct( "__cfaehm_base_exception_t" );
    563567                        assert( exception_decl );
    564568                        Type * exceptType = new PointerType( noQualifiers, new StructInstType( noQualifiers, const_cast<StructDecl *>(exception_decl) ) );
     
    964968        namespace {
    965969                /// Finds deleted expressions in an expression tree
    966                 struct DeleteFinder_new final : public ast::WithShortCircuiting {
    967                         const ast::DeletedExpr * delExpr = nullptr;
     970                struct DeleteFinder_new final : public ast::WithShortCircuiting, public ast::WithVisitorRef<DeleteFinder_new> {
     971                        const ast::DeletedExpr * result = nullptr;
    968972
    969973                        void previsit( const ast::DeletedExpr * expr ) {
    970                                 if ( delExpr ) { visit_children = false; }
    971                                 else { delExpr = expr; }
    972                         }
    973 
    974                         void previsit( const ast::Expr * ) {
    975                                 if ( delExpr ) { visit_children = false; }
     974                                if ( result ) { visit_children = false; }
     975                                else { result = expr; }
     976                        }
     977
     978                        void previsit( const ast::Expr * expr ) {
     979                                if ( result ) { visit_children = false; }
     980                                if (expr->inferred.hasParams()) {
     981                                        for (auto & imp : expr->inferred.inferParams() ) {
     982                                                imp.second.expr->accept(*visitor);
     983                                        }
     984                                }
    976985                        }
    977986                };
    978987        } // anonymous namespace
    979 
    980988        /// Check if this expression is or includes a deleted expression
    981989        const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) {
    982                 ast::Pass<DeleteFinder_new> finder;
    983                 expr->accept( finder );
    984                 return finder.pass.delExpr;
     990                return ast::Pass<DeleteFinder_new>::read( expr );
    985991        }
    986992
     
    10721078                /// Strips extraneous casts out of an expression
    10731079                struct StripCasts_new final {
    1074                         const ast::Expr * postmutate( const ast::CastExpr * castExpr ) {
     1080                        const ast::Expr * postvisit( const ast::CastExpr * castExpr ) {
    10751081                                if (
    1076                                         castExpr->isGenerated
     1082                                        castExpr->isGenerated == ast::GeneratedCast
    10771083                                        && typesCompatible( castExpr->arg->result, castExpr->result )
    10781084                                ) {
     
    11061112                }
    11071113
    1108                 /// Establish post-resolver invariants for expressions
     1114               
     1115        } // anonymous namespace
     1116/// Establish post-resolver invariants for expressions
    11091117                void finishExpr(
    11101118                        ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env,
     
    11191127                        StripCasts_new::strip( expr );
    11201128                }
    1121         } // anonymous namespace
    1122 
    11231129
    11241130        ast::ptr< ast::Expr > resolveInVoidContext(
     
    11281134
    11291135                // set up and resolve expression cast to void
    1130                 ast::CastExpr * untyped = new ast::CastExpr{ expr };
     1136                ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr };
    11311137                CandidateRef choice = findUnfinishedKindExpression(
    11321138                        untyped, symtab, "", anyCandidate, ResolvMode::withAdjustment() );
     
    11401146        }
    11411147
    1142         namespace {
    1143                 /// Resolve `untyped` to the expression whose candidate is the best match for a `void`
     1148        /// Resolve `untyped` to the expression whose candidate is the best match for a `void`
    11441149                /// context.
    11451150                ast::ptr< ast::Expr > findVoidExpression(
    11461151                        const ast::Expr * untyped, const ast::SymbolTable & symtab
    11471152                ) {
    1148                         resetTyVarRenaming();
    11491153                        ast::TypeEnvironment env;
    11501154                        ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, symtab, env );
     
    11521156                        return newExpr;
    11531157                }
     1158
     1159        namespace {
     1160               
    11541161
    11551162                /// resolve `untyped` to the expression whose candidate satisfies `pred` with the
     
    11631170                        CandidateRef choice =
    11641171                                findUnfinishedKindExpression( untyped, symtab, kind, pred, mode );
    1165                         finishExpr( choice->expr, choice->env, untyped->env );
     1172                        ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env );
    11661173                        return std::move( choice->expr );
    11671174                }
     
    11711178                        const ast::Expr * untyped, const ast::SymbolTable & symtab
    11721179                ) {
    1173                         return findKindExpression( untyped, symtab );
     1180                        Stats::ResolveTime::start( untyped );
     1181                        auto res = findKindExpression( untyped, symtab );
     1182                        Stats::ResolveTime::stop();
     1183                        return res;
    11741184                }
    11751185        } // anonymous namespace
    11761186
    1177                 ast::ptr< ast::Expr > findSingleExpression(
    1178                         const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab
    1179                 ) {
    1180                         assert( untyped && type );
    1181                         ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };
    1182                         ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, symtab );
    1183                         removeExtraneousCast( newExpr, symtab );
    1184                         return newExpr;
    1185                 }
     1187        ast::ptr< ast::Expr > findSingleExpression(
     1188                const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab
     1189        ) {
     1190                assert( untyped && type );
     1191                ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };
     1192                ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, symtab );
     1193                removeExtraneousCast( newExpr, symtab );
     1194                return newExpr;
     1195        }
    11861196
    11871197        namespace {
     1198                bool structOrUnion( const Candidate & i ) {
     1199                        const ast::Type * t = i.expr->result->stripReferences();
     1200                        return dynamic_cast< const ast::StructInstType * >( t ) || dynamic_cast< const ast::UnionInstType * >( t );
     1201                }
    11881202                /// Predicate for "Candidate has integral type"
    11891203                bool hasIntegralType( const Candidate & i ) {
     
    12211235                template<typename Iter>
    12221236                inline bool nextMutex( Iter & it, const Iter & end ) {
    1223                         while ( it != end && ! (*it)->get_type()->is_mutex() ) { ++it; }
     1237                        while ( it != end && ! (*it)->is_mutex() ) { ++it; }
    12241238                        return it != end;
    12251239                }
     
    12331247                ast::ptr< ast::Type > functionReturn = nullptr;
    12341248                ast::CurrentObject currentObject;
     1249                // for work previously in GenInit
     1250                static InitTweak::ManagedTypes_new managedTypes;
     1251
    12351252                bool inEnumDecl = false;
    12361253
    12371254        public:
     1255                static size_t traceId;
    12381256                Resolver_new() = default;
    12391257                Resolver_new( const ast::SymbolTable & syms ) { symtab = syms; }
    12401258
    1241                 void previsit( const ast::FunctionDecl * );
     1259                const ast::FunctionDecl * previsit( const ast::FunctionDecl * );
    12421260                const ast::FunctionDecl * postvisit( const ast::FunctionDecl * );
    1243                 void previsit( const ast::ObjectDecl * );
     1261                const ast::ObjectDecl * previsit( const ast::ObjectDecl * );
     1262                void previsit( const ast::AggregateDecl * );
     1263                void previsit( const ast::StructDecl * );
    12441264                void previsit( const ast::EnumDecl * );
    12451265                const ast::StaticAssertDecl * previsit( const ast::StaticAssertDecl * );
     
    12601280                const ast::ThrowStmt *       previsit( const ast::ThrowStmt * );
    12611281                const ast::CatchStmt *       previsit( const ast::CatchStmt * );
     1282                const ast::CatchStmt *       postvisit( const ast::CatchStmt * );
    12621283                const ast::WaitForStmt *     previsit( const ast::WaitForStmt * );
     1284                const ast::WithStmt *        previsit( const ast::WithStmt * );
    12631285
    12641286                const ast::SingleInit *      previsit( const ast::SingleInit * );
    12651287                const ast::ListInit *        previsit( const ast::ListInit * );
    12661288                const ast::ConstructorInit * previsit( const ast::ConstructorInit * );
     1289
     1290                void resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd);
     1291
     1292                void beginScope() { managedTypes.beginScope(); }
     1293                void endScope() { managedTypes.endScope(); }
     1294                bool on_error(ast::ptr<ast::Decl> & decl);
    12671295        };
    1268 
    1269         void resolve( std::list< ast::ptr<ast::Decl> >& translationUnit ) {
    1270                 ast::Pass< Resolver_new > resolver;
    1271                 accept_all( translationUnit, resolver );
     1296        // size_t Resolver_new::traceId = Stats::Heap::new_stacktrace_id("Resolver");
     1297
     1298        InitTweak::ManagedTypes_new Resolver_new::managedTypes;
     1299
     1300        void resolve( ast::TranslationUnit& translationUnit ) {
     1301                ast::Pass< Resolver_new >::run( translationUnit );
    12721302        }
    12731303
     
    12801310        }
    12811311
    1282         ast::ptr< ast::Expr > resolveStmtExpr(
     1312        const ast::Expr * resolveStmtExpr(
    12831313                const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab
    12841314        ) {
    12851315                assert( stmtExpr );
    12861316                ast::Pass< Resolver_new > resolver{ symtab };
    1287                 ast::ptr< ast::Expr > ret = stmtExpr;
    1288                 ret = ret->accept( resolver );
    1289                 strict_dynamic_cast< ast::StmtExpr * >( ret.get_and_mutate() )->computeResult();
     1317                auto ret = mutate(stmtExpr->accept(resolver));
     1318                strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult();
    12901319                return ret;
    12911320        }
    12921321
    1293         void Resolver_new::previsit( const ast::FunctionDecl * functionDecl ) {
     1322        namespace {
     1323                const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ast::SymbolTable & symtab) {
     1324                        std::string name = attr->normalizedName();
     1325                        if (name == "constructor" || name == "destructor") {
     1326                                if (attr->params.size() == 1) {
     1327                                        auto arg = attr->params.front();
     1328                                        auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicType::LongLongSignedInt ), symtab );
     1329                                        auto result = eval(arg);
     1330
     1331                                        auto mutAttr = mutate(attr);
     1332                                        mutAttr->params.front() = resolved;
     1333                                        if (! result.second) {
     1334                                                SemanticWarning(loc, Warning::GccAttributes,
     1335                                                        toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) );
     1336                                        }
     1337                                        else {
     1338                                                auto priority = result.first;
     1339                                                if (priority < 101) {
     1340                                                        SemanticWarning(loc, Warning::GccAttributes,
     1341                                                                toCString( name, " priorities from 0 to 100 are reserved for the implementation" ) );
     1342                                                } else if (priority < 201 && ! buildingLibrary()) {
     1343                                                        SemanticWarning(loc, Warning::GccAttributes,
     1344                                                                toCString( name, " priorities from 101 to 200 are reserved for the implementation" ) );
     1345                                                }
     1346                                        }
     1347                                        return mutAttr;
     1348                                } else if (attr->params.size() > 1) {
     1349                                        SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) );
     1350                                } else {
     1351                                        SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) );
     1352                                }
     1353                        }
     1354                        return attr;
     1355                }
     1356        }
     1357
     1358        const ast::FunctionDecl * Resolver_new::previsit( const ast::FunctionDecl * functionDecl ) {
    12941359                GuardValue( functionReturn );
     1360
     1361                assert (functionDecl->unique());
     1362                if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) {
     1363                        SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations");
     1364                }
     1365
     1366                if (!functionDecl->isTypeFixed) {
     1367                        auto mutDecl = mutate(functionDecl);
     1368                        auto mutType = mutDecl->type.get_and_mutate();
     1369
     1370                        for (auto & attr: mutDecl->attributes) {
     1371                                attr = handleAttribute(mutDecl->location, attr, symtab);
     1372                        }
     1373
     1374                        // handle assertions
     1375
     1376                        symtab.enterScope();
     1377                        mutType->forall.clear();
     1378                        mutType->assertions.clear();
     1379                        for (auto & typeParam : mutDecl->type_params) {
     1380                                symtab.addType(typeParam);
     1381                                mutType->forall.emplace_back(new ast::TypeInstType(typeParam->name, typeParam));
     1382                        }
     1383                        for (auto & asst : mutDecl->assertions) {
     1384                                asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab);
     1385                                symtab.addId(asst);
     1386                                mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));
     1387                        }
     1388
     1389                        // temporarily adds params to symbol table.
     1390                        // actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl)
     1391
     1392                        std::vector<ast::ptr<ast::Type>> paramTypes;
     1393                        std::vector<ast::ptr<ast::Type>> returnTypes;
     1394
     1395                        for (auto & param : mutDecl->params) {
     1396                                param = fixObjectType(param.strict_as<ast::ObjectDecl>(), symtab);
     1397                                symtab.addId(param);
     1398                                paramTypes.emplace_back(param->get_type());
     1399                        }
     1400                        for (auto & ret : mutDecl->returns) {
     1401                                ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), symtab);
     1402                                returnTypes.emplace_back(ret->get_type());
     1403                        }
     1404                        // since function type in decl is just a view of param types, need to update that as well
     1405                        mutType->params = std::move(paramTypes);
     1406                        mutType->returns = std::move(returnTypes);
     1407
     1408                        auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));
     1409
     1410                        std::list<ast::ptr<ast::Stmt>> newStmts;
     1411                        resolveWithExprs (mutDecl->withExprs, newStmts);
     1412
     1413                        if (mutDecl->stmts) {
     1414                                auto mutStmt = mutDecl->stmts.get_and_mutate();
     1415                                mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts));
     1416                                mutDecl->stmts = mutStmt;
     1417                        }
     1418
     1419                        symtab.leaveScope();
     1420
     1421                        mutDecl->type = renamedType;
     1422                        mutDecl->mangleName = Mangle::mangle(mutDecl);
     1423                        mutDecl->isTypeFixed = true;
     1424                        functionDecl = mutDecl;
     1425                }
     1426                managedTypes.handleDWT(functionDecl);
     1427
    12951428                functionReturn = extractResultType( functionDecl->type );
     1429                return functionDecl;
    12961430        }
    12971431
     
    12991433                // default value expressions have an environment which shouldn't be there and trips up
    13001434                // later passes.
    1301                 ast::ptr< ast::FunctionDecl > ret = functionDecl;
    1302                 for ( unsigned i = 0; i < functionDecl->type->params.size(); ++i ) {
    1303                         const ast::ptr<ast::DeclWithType> & d = functionDecl->type->params[i];
    1304 
    1305                         if ( const ast::ObjectDecl * obj = d.as< ast::ObjectDecl >() ) {
     1435                assert( functionDecl->unique() );
     1436                ast::FunctionType * mutType = mutate( functionDecl->type.get() );
     1437
     1438                for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) {
     1439                        if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) {
    13061440                                if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) {
    13071441                                        if ( init->value->env == nullptr ) continue;
    13081442                                        // clone initializer minus the initializer environment
    1309                                         ast::chain_mutate( ret )
    1310                                                 ( &ast::FunctionDecl::type )
    1311                                                         ( &ast::FunctionType::params )[i]
    1312                                                                 ( &ast::ObjectDecl::init )
    1313                                                                         ( &ast::SingleInit::value )->env = nullptr;
    1314 
    1315                                         assert( functionDecl != ret.get() || functionDecl->unique() );
    1316                                         assert( ! ret->type->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env );
     1443                                        auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() );
     1444                                        auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() );
     1445                                        auto mutValue = mutate( mutInit->value.get() );
     1446
     1447                                        mutValue->env = nullptr;
     1448                                        mutInit->value = mutValue;
     1449                                        mutParam->init = mutInit;
     1450                                        mutType->params[i] = mutParam;
     1451
     1452                                        assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env);
    13171453                                }
    13181454                        }
    13191455                }
    1320                 return ret.get();
    1321         }
    1322 
    1323         void Resolver_new::previsit( const ast::ObjectDecl * objectDecl ) {
     1456                mutate_field(functionDecl, &ast::FunctionDecl::type, mutType);
     1457                return functionDecl;
     1458        }
     1459
     1460        const ast::ObjectDecl * Resolver_new::previsit( const ast::ObjectDecl * objectDecl ) {
    13241461                // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()],
    13251462                // class-variable `initContext` is changed multiple times because the LHS is analyzed
     
    13291466                // selecting the RHS.
    13301467                GuardValue( currentObject );
    1331                 currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() };
     1468
    13321469                if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) {
    13331470                        // enumerator initializers should not use the enum type to initialize, since the
    13341471                        // enum type is still incomplete at this point. Use `int` instead.
     1472                        objectDecl = fixObjectType(objectDecl, symtab);
    13351473                        currentObject = ast::CurrentObject{
    13361474                                objectDecl->location, new ast::BasicType{ ast::BasicType::SignedInt } };
    13371475                }
     1476                else {
     1477                        if (!objectDecl->isTypeFixed) {
     1478                                auto newDecl = fixObjectType(objectDecl, symtab);
     1479                                auto mutDecl = mutate(newDecl);
     1480
     1481                                // generate CtorInit wrapper when necessary.
     1482                                // in certain cases, fixObjectType is called before reaching
     1483                                // this object in visitor pass, thus disabling CtorInit codegen.
     1484                                // this happens on aggregate members and function parameters.
     1485                                if ( InitTweak::tryConstruct( mutDecl ) && ( managedTypes.isManaged( mutDecl ) || ((! isInFunction() || mutDecl->storage.is_static ) && ! InitTweak::isConstExpr( mutDecl->init ) ) ) ) {
     1486                                        // constructed objects cannot be designated
     1487                                        if ( InitTweak::isDesignated( mutDecl->init ) ) SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object. If this is really what you want, then initialize with @=.\n" );
     1488                                        // constructed objects should not have initializers nested too deeply
     1489                                        if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " );
     1490
     1491                                        mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl );
     1492                                }
     1493
     1494                                objectDecl = mutDecl;
     1495                        }
     1496                        currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() };
     1497                }
     1498
     1499                return objectDecl;
     1500        }
     1501
     1502        void Resolver_new::previsit( const ast::AggregateDecl * _aggDecl ) {
     1503                auto aggDecl = mutate(_aggDecl);
     1504                assertf(aggDecl == _aggDecl, "type declarations must be unique");
     1505
     1506                for (auto & member: aggDecl->members) {
     1507                        // nested type decls are hoisted already. no need to do anything
     1508                        if (auto obj = member.as<ast::ObjectDecl>()) {
     1509                                member = fixObjectType(obj, symtab);
     1510                        }
     1511                }
     1512        }
     1513
     1514        void Resolver_new::previsit( const ast::StructDecl * structDecl ) {
     1515                previsit(static_cast<const ast::AggregateDecl *>(structDecl));
     1516                managedTypes.handleStruct(structDecl);
    13381517        }
    13391518
     
    13411520                // in case we decide to allow nested enums
    13421521                GuardValue( inEnumDecl );
    1343                 inEnumDecl = false;
    1344         }
     1522                inEnumDecl = true;
     1523                // don't need to fix types for enum fields
     1524        }
     1525
    13451526
    13461527        const ast::StaticAssertDecl * Resolver_new::previsit(
     
    13551536        const PtrType * handlePtrType( const PtrType * type, const ast::SymbolTable & symtab ) {
    13561537                if ( type->dimension ) {
    1357                         #warning should use new equivalent to Validate::SizeType rather than sizeType here
    1358                         ast::ptr< ast::Type > sizeType = new ast::BasicType{ ast::BasicType::LongUnsignedInt };
     1538                        ast::ptr< ast::Type > sizeType = ast::sizeType;
    13591539                        ast::mutate_field(
    13601540                                type, &PtrType::dimension,
     
    14771657                if ( throwStmt->expr ) {
    14781658                        const ast::StructDecl * exceptionDecl =
    1479                                 symtab.lookupStruct( "__cfaabi_ehm__base_exception_t" );
     1659                                symtab.lookupStruct( "__cfaehm_base_exception_t" );
    14801660                        assert( exceptionDecl );
    14811661                        ast::ptr< ast::Type > exceptType =
     
    14891669
    14901670        const ast::CatchStmt * Resolver_new::previsit( const ast::CatchStmt * catchStmt ) {
    1491                 // TODO: This will need a fix for the decl/cond scoping problem.
     1671                // Until we are very sure this invarent (ifs that move between passes have thenPart)
     1672                // holds, check it. This allows a check for when to decode the mangling.
     1673                if ( auto ifStmt = catchStmt->body.as<ast::IfStmt>() ) {
     1674                        assert( ifStmt->thenPart );
     1675                }
     1676                // Encode the catchStmt so the condition can see the declaration.
    14921677                if ( catchStmt->cond ) {
    1493                         ast::ptr< ast::Type > boolType = new ast::BasicType{ ast::BasicType::Bool };
    1494                         catchStmt = ast::mutate_field(
    1495                                 catchStmt, &ast::CatchStmt::cond,
    1496                                 findSingleExpression( catchStmt->cond, boolType, symtab ) );
     1678                        ast::CatchStmt * stmt = mutate( catchStmt );
     1679                        stmt->body = new ast::IfStmt( stmt->location, stmt->cond, nullptr, stmt->body );
     1680                        stmt->cond = nullptr;
     1681                        return stmt;
     1682                }
     1683                return catchStmt;
     1684        }
     1685
     1686        const ast::CatchStmt * Resolver_new::postvisit( const ast::CatchStmt * catchStmt ) {
     1687                // Decode the catchStmt so everything is stored properly.
     1688                const ast::IfStmt * ifStmt = catchStmt->body.as<ast::IfStmt>();
     1689                if ( nullptr != ifStmt && nullptr == ifStmt->thenPart ) {
     1690                        assert( ifStmt->cond );
     1691                        assert( ifStmt->elsePart );
     1692                        ast::CatchStmt * stmt = ast::mutate( catchStmt );
     1693                        stmt->cond = ifStmt->cond;
     1694                        stmt->body = ifStmt->elsePart;
     1695                        // ifStmt should be implicately deleted here.
     1696                        return stmt;
    14971697                }
    14981698                return catchStmt;
     
    16111811                                                                // Check if the argument matches the parameter type in the current
    16121812                                                                // scope
    1613                                                                 ast::ptr< ast::Type > paramType = (*param)->get_type();
     1813                                                                // ast::ptr< ast::Type > paramType = (*param)->get_type();
    16141814                                                                if (
    16151815                                                                        ! unify(
    1616                                                                                 arg->expr->result, paramType, resultEnv, need, have, open,
     1816                                                                                arg->expr->result, *param, resultEnv, need, have, open,
    16171817                                                                                symtab )
    16181818                                                                ) {
     
    16211821                                                                        ss << "candidate function not viable: no known conversion "
    16221822                                                                                "from '";
    1623                                                                         ast::print( ss, (*param)->get_type() );
     1823                                                                        ast::print( ss, *param );
    16241824                                                                        ss << "' to '";
    16251825                                                                        ast::print( ss, arg->expr->result );
     
    17511951        }
    17521952
     1953        const ast::WithStmt * Resolver_new::previsit( const ast::WithStmt * withStmt ) {
     1954                auto mutStmt = mutate(withStmt);
     1955                resolveWithExprs(mutStmt->exprs, stmtsToAddBefore);
     1956                return mutStmt;
     1957        }
     1958
     1959        void Resolver_new::resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd) {
     1960                for (auto & expr : exprs) {
     1961                        // only struct- and union-typed expressions are viable candidates
     1962                        expr = findKindExpression( expr, symtab, structOrUnion, "with expression" );
     1963
     1964                        // if with expression might be impure, create a temporary so that it is evaluated once
     1965                        if ( Tuples::maybeImpure( expr ) ) {
     1966                                static UniqueName tmpNamer( "_with_tmp_" );
     1967                                const CodeLocation loc = expr->location;
     1968                                auto tmp = new ast::ObjectDecl(loc, tmpNamer.newName(), expr->result, new ast::SingleInit(loc, expr ) );
     1969                                expr = new ast::VariableExpr( loc, tmp );
     1970                                stmtsToAdd.push_back( new ast::DeclStmt(loc, tmp ) );
     1971                                if ( InitTweak::isConstructable( tmp->type ) ) {
     1972                                        // generate ctor/dtor and resolve them
     1973                                        tmp->init = InitTweak::genCtorInit( loc, tmp );
     1974                                }
     1975                                // since tmp is freshly created, this should modify tmp in-place
     1976                                tmp->accept( *visitor );
     1977                        }
     1978                }
     1979        }
    17531980
    17541981
     
    18462073        }
    18472074
     2075        // suppress error on autogen functions and mark invalid autogen as deleted.
     2076        bool Resolver_new::on_error(ast::ptr<ast::Decl> & decl) {
     2077                if (auto functionDecl = decl.as<ast::FunctionDecl>()) {
     2078                        // xxx - can intrinsic gen ever fail?
     2079                        if (functionDecl->linkage == ast::Linkage::AutoGen) {
     2080                                auto mutDecl = mutate(functionDecl);
     2081                                mutDecl->isDeleted = true;
     2082                                mutDecl->stmts = nullptr;
     2083                                decl = mutDecl;
     2084                                return false;
     2085                        }
     2086                }
     2087                return true;
     2088        }
     2089
    18482090} // namespace ResolvExpr
    18492091
  • src/ResolvExpr/Resolver.h

    r3c64c668 r58fe85a  
    3535        class StmtExpr;
    3636        class SymbolTable;
     37        struct TranslationUnit;
    3738        class Type;
    3839        class TypeEnvironment;
     
    5556
    5657        /// Checks types and binds syntactic constructs to typed representations
    57         void resolve( std::list< ast::ptr<ast::Decl> >& translationUnit );
     58        void resolve( ast::TranslationUnit& translationUnit );
    5859        /// Searches expr and returns the first DeletedExpr found, otherwise nullptr
    5960        const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr );
     
    6263        ast::ptr< ast::Expr > resolveInVoidContext(
    6364                const ast::Expr * expr, const ast::SymbolTable & symtab, ast::TypeEnvironment & env );
    64         /// Resolve `untyped` to the single expression whose candidate is the best match for the 
     65        /// Resolve `untyped` to the single expression whose candidate is the best match for the
    6566        /// given type.
    6667        ast::ptr< ast::Expr > findSingleExpression(
    6768                const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab );
     69        ast::ptr< ast::Expr > findVoidExpression(
     70                const ast::Expr * untyped, const ast::SymbolTable & symtab);
    6871        /// Resolves a constructor init expression
    69         ast::ptr< ast::Init > resolveCtorInit( 
     72        ast::ptr< ast::Init > resolveCtorInit(
    7073                const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab );
    71         /// Resolves a statement expression
    72         ast::ptr< ast::Expr > resolveStmtExpr(
     74        /// Resolves a statement expression 
     75        const ast::Expr * resolveStmtExpr(
    7376                const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab );
    7477} // namespace ResolvExpr
  • src/ResolvExpr/SatisfyAssertions.cpp

    r3c64c668 r58fe85a  
    99// Author           : Aaron B. Moss
    1010// Created On       : Mon Jun 10 17:45:00 2019
    11 // Last Modified By : Aaron B. Moss
    12 // Last Modified On : Mon Jun 10 17:45:00 2019
    13 // Update Count     : 1
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Oct  1 13:56:00 2019
     13// Update Count     : 2
    1414//
    1515
     
    5757                ast::UniqueId resnSlot;          ///< Slot for any recursive assertion IDs
    5858
    59                 AssnCandidate( 
    60                         const ast::SymbolTable::IdData c, const ast::Type * at, ast::TypeEnvironment && e, 
     59                AssnCandidate(
     60                        const ast::SymbolTable::IdData c, const ast::Type * at, ast::TypeEnvironment && e,
    6161                        ast::AssertionSet && h, ast::AssertionSet && n, ast::OpenVarSet && o, ast::UniqueId rs )
    62                 : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ), 
     62                : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ),
    6363                  need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {}
    6464        };
     
    6969        /// Reference to a single deferred item
    7070        struct DeferRef {
    71                 const ast::DeclWithType * decl;
     71                const ast::VariableExpr * expr;
    7272                const ast::AssertionSetValue & info;
    7373                const AssnCandidate & match;
    7474        };
    75        
    76         /// Wrapper for the deferred items from a single assertion satisfaction. 
     75
     76        /// Wrapper for the deferred items from a single assertion satisfaction.
    7777        /// Acts like an indexed list of DeferRef
    7878        struct DeferItem {
    79                 const ast::DeclWithType * decl;
     79                const ast::VariableExpr * expr;
    8080                const ast::AssertionSetValue & info;
    8181                AssnCandidateList matches;
    8282
    83                 DeferItem( 
    84                         const ast::DeclWithType * d, const ast::AssertionSetValue & i, AssnCandidateList && ms )
    85                 : decl( d ), info( i ), matches( std::move( ms ) ) {}
     83                DeferItem(
     84                        const ast::VariableExpr * d, const ast::AssertionSetValue & i, AssnCandidateList && ms )
     85                : expr( d ), info( i ), matches( std::move( ms ) ) {}
    8686
    8787                bool empty() const { return matches.empty(); }
     
    8989                AssnCandidateList::size_type size() const { return matches.size(); }
    9090
    91                 DeferRef operator[] ( unsigned i ) const { return { decl, info, matches[i] }; }
     91                DeferRef operator[] ( unsigned i ) const { return { expr, info, matches[i] }; }
    9292        };
    9393
     
    117117                /// Initial satisfaction state for a candidate
    118118                SatState( CandidateRef & c, const ast::SymbolTable & syms )
    119                 : cand( c ), need(), newNeed(), deferred(), inferred(), costs{ Cost::zero }, 
     119                : cand( c ), need(), newNeed(), deferred(), inferred(), costs{ Cost::zero },
    120120                  symtab( syms ) { need.swap( c->need ); }
    121                
     121
    122122                /// Update satisfaction state for next step after previous state
    123123                SatState( SatState && o, IterateFlag )
    124                 : cand( std::move( o.cand ) ), need( o.newNeed.begin(), o.newNeed.end() ), newNeed(), 
    125                   deferred(), inferred( std::move( o.inferred ) ), costs( std::move( o.costs ) ), 
     124                : cand( std::move( o.cand ) ), need( o.newNeed.begin(), o.newNeed.end() ), newNeed(),
     125                  deferred(), inferred( std::move( o.inferred ) ), costs( std::move( o.costs ) ),
    126126                  symtab( o.symtab ) { costs.emplace_back( Cost::zero ); }
    127                
     127
    128128                /// Field-wise next step constructor
    129129                SatState(
    130                         CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs, 
     130                        CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs,
    131131                        ast::SymbolTable && syms )
    132                 : cand( std::move( c ) ), need( nn.begin(), nn.end() ), newNeed(), deferred(), 
     132                : cand( std::move( c ) ), need( nn.begin(), nn.end() ), newNeed(), deferred(),
    133133                  inferred( std::move( i ) ), costs( std::move( cs ) ), symtab( std::move( syms ) )
    134134                  { costs.emplace_back( Cost::zero ); }
     
    138138        void addToSymbolTable( const ast::AssertionSet & have, ast::SymbolTable & symtab ) {
    139139                for ( auto & i : have ) {
    140                         if ( i.second.isUsed ) { symtab.addId( i.first ); }
     140                        if ( i.second.isUsed ) { symtab.addId( i.first->var ); }
    141141                }
    142142        }
    143143
    144144        /// Binds a single assertion, updating satisfaction state
    145         void bindAssertion( 
    146                 const ast::DeclWithType * decl, const ast::AssertionSetValue & info, CandidateRef & cand,
     145        void bindAssertion(
     146                const ast::VariableExpr * expr, const ast::AssertionSetValue & info, CandidateRef & cand,
    147147                AssnCandidate & match, InferCache & inferred
    148148        ) {
    149149                const ast::DeclWithType * candidate = match.cdata.id;
    150                 assertf( candidate->uniqueId, 
     150                assertf( candidate->uniqueId,
    151151                        "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() );
    152                
     152
    153153                ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cvtCost );
    154154                varExpr->result = match.adjType;
     
    156156
    157157                // place newly-inferred assertion in proper location in cache
    158                 inferred[ info.resnSlot ][ decl->uniqueId ] = ast::ParamEntry{
    159                         candidate->uniqueId, candidate, match.adjType, decl->get_type(), varExpr };
     158                inferred[ info.resnSlot ][ expr->var->uniqueId ] = ast::ParamEntry{
     159                        candidate->uniqueId, candidate, match.adjType, expr->result, varExpr };
    160160        }
    161161
     
    167167                // find candidates that unify with the desired type
    168168                AssnCandidateList matches;
    169                 for ( const ast::SymbolTable::IdData & cdata : sat.symtab.lookupId( assn.first->name ) ) {
     169
     170                std::vector<ast::SymbolTable::IdData> candidates;
     171                auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->var->name);
     172                if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {
     173                        // prefilter special decls by argument type, if already known
     174                        ast::ptr<ast::Type> thisArgType = assn.first->result.strict_as<ast::PointerType>()->base
     175                                .strict_as<ast::FunctionType>()->params[0]
     176                                .strict_as<ast::ReferenceType>()->base;
     177                        sat.cand->env.apply(thisArgType);
     178
     179                        std::string otypeKey = "";
     180                        if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer;
     181                        else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams);
     182
     183                        candidates = sat.symtab.specialLookupId(kind, otypeKey);
     184                }
     185                else {
     186                        candidates = sat.symtab.lookupId(assn.first->var->name);
     187                }
     188                for ( const ast::SymbolTable::IdData & cdata : candidates ) {
    170189                        const ast::DeclWithType * candidate = cdata.id;
     190
     191                        // ignore deleted candidates.
     192                        // NOTE: this behavior is different from main resolver.
     193                        // further investigations might be needed to determine
     194                        // if we should implement the same rule here
     195                        // (i.e. error if unique best match is deleted)
     196                        if (candidate->isDeleted && candidate->linkage == ast::Linkage::AutoGen) continue;
    171197
    172198                        // build independent unification context for candidate
     
    174200                        ast::TypeEnvironment newEnv{ sat.cand->env };
    175201                        ast::OpenVarSet newOpen{ sat.cand->open };
    176                         ast::ptr< ast::Type > toType = assn.first->get_type();
    177                         ast::ptr< ast::Type > adjType = 
    178                                 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) );
     202                        ast::ptr< ast::Type > toType = assn.first->result;
     203                        ast::ptr< ast::Type > adjType =
     204                                renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ), GEN_USAGE, false );
    179205
    180206                        // only keep candidates which unify
     
    187213                                }
    188214
    189                                 matches.emplace_back( 
    190                                         cdata, adjType, std::move( newEnv ), std::move( newNeed ), std::move( have ),
     215                                matches.emplace_back(
     216                                        cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
    191217                                        std::move( newOpen ), crntResnSlot );
    192218                        }
     
    229255                InferMatcher( InferCache & inferred ) : inferred( inferred ) {}
    230256
    231                 const ast::Expr * postmutate( const ast::Expr * expr ) {
     257                const ast::Expr * postvisit( const ast::Expr * expr ) {
    232258                        // Skip if no slots to find
    233                         if ( expr->inferred.mode != ast::Expr::InferUnion::Slots ) return expr;
    234 
     259                        if ( !expr->inferred.hasSlots() ) return expr;
     260                        // if ( expr->inferred.mode != ast::Expr::InferUnion::Slots ) return expr;
     261                        std::vector<UniqueId> missingSlots;
    235262                        // find inferred parameters for resolution slots
    236                         ast::InferredParams newInferred;
     263                        ast::InferredParams * newInferred = new ast::InferredParams();
    237264                        for ( UniqueId slot : expr->inferred.resnSlots() ) {
    238265                                // fail if no matching assertions found
    239266                                auto it = inferred.find( slot );
    240267                                if ( it == inferred.end() ) {
    241                                         assert(!"missing assertion");
     268                                        // std::cerr << "missing assertion " << slot << std::endl;
     269                                        missingSlots.push_back(slot);
     270                                        continue;
    242271                                }
    243272
     
    245274                                for ( auto & entry : it->second ) {
    246275                                        // recurse on inferParams of resolved expressions
    247                                         entry.second.expr = postmutate( entry.second.expr );
    248                                         auto res = newInferred.emplace( entry );
     276                                        entry.second.expr = postvisit( entry.second.expr );
     277                                        auto res = newInferred->emplace( entry );
    249278                                        assert( res.second && "all assertions newly placed" );
    250279                                }
     
    252281
    253282                        ast::Expr * ret = mutate( expr );
    254                         ret->inferred.set_inferParams( std::move( newInferred ) );
     283                        ret->inferred.set_inferParams( newInferred );
     284                        if (!missingSlots.empty()) ret->inferred.resnSlots() = missingSlots;
    255285                        return ret;
    256286                }
    257287        };
    258288
    259         /// Replace ResnSlots with InferParams and add alternative to output list, if it meets pruning 
     289        /// Replace ResnSlots with InferParams and add alternative to output list, if it meets pruning
    260290        /// threshold.
    261         void finalizeAssertions( 
    262                 CandidateRef & cand, InferCache & inferred, PruneMap & thresholds, CostVec && costs, 
    263                 CandidateList & out 
     291        void finalizeAssertions(
     292                CandidateRef & cand, InferCache & inferred, PruneMap & thresholds, CostVec && costs,
     293                CandidateList & out
    264294        ) {
    265295                // prune if cheaper alternative for same key has already been generated
     
    278308        }
    279309
    280         /// Combo iterator that combines candidates into an output list, merging their environments. 
    281         /// Rejects an appended candidate if environments cannot be merged. See `Common/FilterCombos.h` 
     310        /// Combo iterator that combines candidates into an output list, merging their environments.
     311        /// Rejects an appended candidate if environments cannot be merged. See `Common/FilterCombos.h`
    282312        /// for description of "combo iterator".
    283313        class CandidateEnvMerger {
     
    299329                        Cost cost;
    300330
    301                         OutType( 
    302                                 const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 
     331                        OutType(
     332                                const ast::TypeEnvironment & e, const ast::OpenVarSet & o,
    303333                                const std::vector< DeferRef > & as, const ast::SymbolTable & symtab )
    304334                        : env( e ), open( o ), assns( as ), cost( Cost::zero ) {
     
    306336                                for ( const DeferRef & assn : assns ) {
    307337                                        // compute conversion cost from satisfying decl to assertion
    308                                         cost += computeConversionCost( 
    309                                                 assn.match.adjType, assn.decl->get_type(), symtab, env );
    310                                        
     338                                        cost += computeConversionCost(
     339                                                assn.match.adjType, assn.expr->result, false, symtab, env );
     340
    311341                                        // mark vars+specialization on function-type assertions
    312                                         const ast::FunctionType * func = 
     342                                        const ast::FunctionType * func =
    313343                                                GenPoly::getFunctionType( assn.match.cdata.id->get_type() );
    314344                                        if ( ! func ) continue;
    315345
    316                                         for ( const ast::DeclWithType * param : func->params ) {
    317                                                 cost.decSpec( specCost( param->get_type() ) );
     346                                        for ( const auto & param : func->params ) {
     347                                                cost.decSpec( specCost( param ) );
    318348                                        }
    319                                        
     349
    320350                                        cost.incVar( func->forall.size() );
    321                                        
    322                                         for ( const ast::TypeDecl * td : func->forall ) {
    323                                                 cost.decSpec( td->assertions.size() );
    324                                         }
     351
     352                                        cost.decSpec( func->assertions.size() );
    325353                                }
    326354                        }
     
    329357                };
    330358
    331                 CandidateEnvMerger( 
    332                         const ast::TypeEnvironment & env, const ast::OpenVarSet & open, 
     359                CandidateEnvMerger(
     360                        const ast::TypeEnvironment & env, const ast::OpenVarSet & open,
    333361                        const ast::SymbolTable & syms )
    334362                : crnt(), envs{ env }, opens{ open }, symtab( syms ) {}
     
    357385
    358386        /// Limit to depth of recursion of assertion satisfaction
    359         static const int recursionLimit = 4;
     387        static const int recursionLimit = 8;
    360388        /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of
    361389        static const int deferLimit = 10;
    362390} // anonymous namespace
    363391
    364 void satisfyAssertions( 
    365         CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 
     392void satisfyAssertions(
     393        CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out,
    366394        std::vector<std::string> & errors
    367395) {
     
    389417                        if ( it != thresholds.end() && it->second < sat.costs ) goto nextSat;
    390418
    391                         // make initial pass at matching assertions
    392                         for ( auto & assn : sat.need ) {
    393                                 // fail early if any assertion is not satisfiable
    394                                 if ( ! satisfyAssertion( assn, sat ) ) {
     419                        // should a limit be imposed? worst case here is O(n^2) but very unlikely to happen.
     420                        for (unsigned resetCount = 0; ; ++resetCount) {
     421                                ast::AssertionList next;
     422                                resetTyVarRenaming();
     423                                // make initial pass at matching assertions
     424                                for ( auto & assn : sat.need ) {
     425                                        // fail early if any assertion is not satisfiable
     426                                        if ( ! satisfyAssertion( assn, sat ) ) {
     427                                                next.emplace_back(assn);
     428                                                // goto nextSat;
     429                                        }
     430                                }
     431                                // success
     432                                if (next.empty()) break;
     433                                // fail if nothing resolves
     434                                else if (next.size() == sat.need.size()) {
    395435                                        Indenter tabs{ 3 };
    396436                                        std::ostringstream ss;
     
    398438                                        print( ss, *sat.cand, ++tabs );
    399439                                        ss << (tabs-1) << "Could not satisfy assertion:\n";
    400                                         ast::print( ss, assn.first, tabs );
     440                                        ast::print( ss, next[0].first, tabs );
    401441
    402442                                        errors.emplace_back( ss.str() );
    403443                                        goto nextSat;
    404444                                }
     445                                sat.need = std::move(next);
    405446                        }
    406447
     
    408449                                // either add successful match or push back next state
    409450                                if ( sat.newNeed.empty() ) {
    410                                         finalizeAssertions( 
     451                                        finalizeAssertions(
    411452                                                sat.cand, sat.inferred, thresholds, std::move( sat.costs ), out );
    412453                                } else {
     
    421462                                ss << (tabs-1) << "Too many non-unique satisfying assignments for assertions:\n";
    422463                                for ( const auto & d : sat.deferred ) {
    423                                         ast::print( ss, d.decl, tabs );
     464                                        ast::print( ss, d.expr, tabs );
    424465                                }
    425466
     
    430471                                std::vector< CandidateEnvMerger::OutType > compatible = filterCombos(
    431472                                        sat.deferred, CandidateEnvMerger{ sat.cand->env, sat.cand->open, sat.symtab } );
    432                                
     473
    433474                                // fail early if no mutually-compatible assertion satisfaction
    434475                                if ( compatible.empty() ) {
     
    439480                                        ss << (tabs-1) << "No mutually-compatible satisfaction for assertions:\n";
    440481                                        for ( const auto& d : sat.deferred ) {
    441                                                 ast::print( ss, d.decl, tabs );
     482                                                ast::print( ss, d.expr, tabs );
    442483                                        }
    443484
     
    453494                                        // set up next satisfaction state
    454495                                        CandidateRef nextCand = std::make_shared<Candidate>(
    455                                                 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 
     496                                                sat.cand->expr, std::move( compat.env ), std::move( compat.open ),
    456497                                                ast::AssertionSet{} /* need moved into satisfaction state */,
    457498                                                sat.cand->cost, sat.cand->cvtCost );
     
    459500                                        ast::AssertionSet nextNewNeed{ sat.newNeed };
    460501                                        InferCache nextInferred{ sat.inferred };
    461                                        
     502
    462503                                        CostVec nextCosts{ sat.costs };
    463504                                        nextCosts.back() += compat.cost;
    464                                                                
     505
    465506                                        ast::SymbolTable nextSymtab{ sat.symtab };
    466507
     
    471512                                                nextNewNeed.insert( match.need.begin(), match.need.end() );
    472513
    473                                                 bindAssertion( r.decl, r.info, nextCand, match, nextInferred );
     514                                                bindAssertion( r.expr, r.info, nextCand, match, nextInferred );
    474515                                        }
    475516
    476517                                        // either add successful match or push back next state
    477518                                        if ( nextNewNeed.empty() ) {
    478                                                 finalizeAssertions( 
     519                                                finalizeAssertions(
    479520                                                        nextCand, nextInferred, thresholds, std::move( nextCosts ), out );
    480521                                        } else {
    481                                                 nextSats.emplace_back( 
    482                                                         std::move( nextCand ), std::move( nextNewNeed ), 
    483                                                         std::move( nextInferred ), std::move( nextCosts ), 
     522                                                nextSats.emplace_back(
     523                                                        std::move( nextCand ), std::move( nextNewNeed ),
     524                                                        std::move( nextInferred ), std::move( nextCosts ),
    484525                                                        std::move( nextSymtab ) );
    485526                                        }
     
    493534                nextSats.clear();
    494535        }
    495        
     536
    496537        // exceeded recursion limit if reaches here
    497538        if ( out.empty() ) {
  • src/ResolvExpr/SatisfyAssertions.hpp

    r3c64c668 r58fe85a  
    2727namespace ResolvExpr {
    2828
    29 /// Recursively satisfies all assertions provided in a candidate; returns true if succeeds
    30 void satisfyAssertions(
    31         CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out,
     29/// Recursively satisfies all assertions provided in a candidate
     30/// returns true if it has been run (candidate has any assertions)
     31void satisfyAssertions(
     32        CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out,
    3233        std::vector<std::string> & errors );
    3334
  • src/ResolvExpr/SpecCost.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Oct 02 15:50:00 2018
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Jun 19 10:43:00 2019
    13 // Update Count     : 2
    14 //
    15 
     12// Last Modified On : Wed Jul  3 11:07:00 2019
     13// Update Count     : 3
     14//
     15
     16#include <cassert>
    1617#include <limits>
    1718#include <list>
     
    129130                        typename std::add_pointer<ast::Type const *(typename T::value_type const &)>::type;
    130131
     132                #warning Should use a standard maybe_accept
     133                void maybe_accept( ast::Type const * type ) {
     134                        if ( type ) {
     135                                auto node = type->accept( *visitor );
     136                                assert( node == nullptr || node == type );
     137                        }
     138                }
     139
    131140                // Update the minimum to the new lowest non-none value.
    132141                template<typename T>
     
    134143                        for ( const auto & node : list ) {
    135144                                count = -1;
    136                                 mapper( node )->accept( *visitor );
     145                                maybe_accept( mapper( node ) );
    137146                                if ( count != -1 && count < minimum ) minimum = count;
    138147                        }
     
    169178                void previsit( const ast::FunctionType * fty ) {
    170179                        int minCount = std::numeric_limits<int>::max();
    171                         updateMinimumPresent( minCount, fty->params, decl_type );
    172                         updateMinimumPresent( minCount, fty->returns, decl_type );
     180                        updateMinimumPresent( minCount, fty->params, type_deref );
     181                        updateMinimumPresent( minCount, fty->returns, type_deref );
    173182                        // Add another level to minCount if set.
    174183                        count = toNoneOrInc( minCount );
     
    208217        }
    209218        ast::Pass<SpecCounter> counter;
    210         type->accept( *counter.pass.visitor );
    211         return counter.pass.get_count();
     219        type->accept( counter );
     220        return counter.core.get_count();
    212221}
    213222
  • src/ResolvExpr/TypeEnvironment.cc

    r3c64c668 r58fe85a  
    2020#include <utility>                     // for pair, move
    2121
     22#include "CompilationState.h"          // for deterministic_output
    2223#include "Common/utility.h"            // for maybeClone
    2324#include "SynTree/Type.h"              // for Type, FunctionType, Type::Fora...
     
    106107
    107108        void EqvClass::print( std::ostream &os, Indenter indent ) const {
    108                 os << "( ";
    109                 std::copy( vars.begin(), vars.end(), std::ostream_iterator< std::string >( os, " " ) );
     109                os << "(";
     110                bool first = true;
     111                for(const auto & var : vars) {
     112                        if(first) first = false;
     113                        else os << " ";
     114                        if( deterministic_output && isUnboundType(var) ) os << "[unbound]";
     115                        else os << var;
     116                }
    110117                os << ")";
    111118                if ( type ) {
     
    235242                // check safely bindable
    236243                if ( r.type && occursIn( r.type, s.vars.begin(), s.vars.end(), *this ) ) return false;
    237                
     244
    238245                // merge classes in
    239246                r.vars.insert( s.vars.begin(), s.vars.end() );
  • src/ResolvExpr/TypeEnvironment.h

    r3c64c668 r58fe85a  
    149149                iterator end() const { return env.end(); }
    150150
     151                auto size() const { return env.size(); }
     152
    151153          private:
    152154                ClassList env;
  • src/ResolvExpr/Unify.cc

    r3c64c668 r58fe85a  
    2525#include <vector>
    2626
     27#include "AST/Copy.hpp"
    2728#include "AST/Decl.hpp"
    2829#include "AST/Node.hpp"
    2930#include "AST/Pass.hpp"
     31#include "AST/Print.hpp"
    3032#include "AST/Type.hpp"
    3133#include "AST/TypeEnvironment.hpp"
     
    135137                findOpenVars( newSecond, open, closed, need, have, FirstOpen );
    136138
    137                 return unifyExact(
    138                         newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
     139                return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
    139140        }
    140141
     
    148149                newFirst->get_qualifiers() = Type::Qualifiers();
    149150                newSecond->get_qualifiers() = Type::Qualifiers();
    150 ///   std::cerr << "first is ";
    151 ///   first->print( std::cerr );
    152 ///   std::cerr << std::endl << "second is ";
    153 ///   second->print( std::cerr );
    154 ///   std::cerr << std::endl << "newFirst is ";
    155 ///   newFirst->print( std::cerr );
    156 ///   std::cerr << std::endl << "newSecond is ";
    157 ///   newSecond->print( std::cerr );
    158 ///   std::cerr << std::endl;
     151
    159152                bool result = unifyExact( newFirst, newSecond, newEnv, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
    160153                delete newFirst;
     
    170163                ast::AssertionSet need, have;
    171164
    172                 ast::ptr<ast::Type> newFirst{ first }, newSecond{ second };
    173                 env.apply( newFirst );
    174                 env.apply( newSecond );
    175                 reset_qualifiers( newFirst );
    176                 reset_qualifiers( newSecond );
     165                ast::Type * newFirst  = shallowCopy( first  );
     166                ast::Type * newSecond = shallowCopy( second );
     167                newFirst ->qualifiers = {};
     168                newSecond->qualifiers = {};
     169                ast::ptr< ast::Type > t1_(newFirst );
     170                ast::ptr< ast::Type > t2_(newSecond);
     171
     172                ast::ptr< ast::Type > subFirst = env.apply(newFirst).node;
     173                ast::ptr< ast::Type > subSecond = env.apply(newSecond).node;
    177174
    178175                return unifyExact(
    179                         newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
     176                        subFirst,
     177                        subSecond,
     178                        newEnv, need, have, open, noWiden(), symtab );
    180179        }
    181180
     
    326325
    327326        void markAssertionSet( AssertionSet &assertions, DeclarationWithType *assert ) {
    328 ///   std::cerr << "assertion set is" << std::endl;
    329 ///   printAssertionSet( assertions, std::cerr, 8 );
    330 ///   std::cerr << "looking for ";
    331 ///   assert->print( std::cerr );
    332 ///   std::cerr << std::endl;
    333327                AssertionSet::iterator i = assertions.find( assert );
    334328                if ( i != assertions.end() ) {
    335 ///     std::cerr << "found it!" << std::endl;
    336329                        i->second.isUsed = true;
    337330                } // if
     
    402395
    403396        template< typename Iterator1, typename Iterator2 >
    404         bool unifyDeclList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) {
     397        bool unifyTypeList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) {
    405398                auto get_type = [](DeclarationWithType * dwt){ return dwt->get_type(); };
    406399                for ( ; list1Begin != list1End && list2Begin != list2End; ++list1Begin, ++list2Begin ) {
     
    496489                                        || flatOther->isTtype()
    497490                        ) {
    498                                 if ( unifyDeclList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
    499                                         if ( unifyDeclList( flatFunc->returnVals.begin(), flatFunc->returnVals.end(), flatOther->returnVals.begin(), flatOther->returnVals.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
     491                                if ( unifyTypeList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
     492                                        if ( unifyTypeList( flatFunc->returnVals.begin(), flatFunc->returnVals.end(), flatOther->returnVals.begin(), flatOther->returnVals.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
    500493
    501494                                                // the original types must be used in mark assertions, since pointer comparisons are used
     
    709702                const ast::SymbolTable & symtab;
    710703        public:
     704                static size_t traceId;
    711705                bool result;
    712706
     
    773767                /// If this isn't done when satifying ttype assertions, then argument lists can have
    774768                /// different size and structure when they should be compatible.
    775                 struct TtypeExpander_new : public ast::WithShortCircuiting {
     769                struct TtypeExpander_new : public ast::WithShortCircuiting, public ast::PureVisitor {
    776770                        ast::TypeEnvironment & tenv;
    777771
     
    779773
    780774                        const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {
    781                                 if ( const ast::EqvClass * clz = tenv.lookup( typeInst->name ) ) {
     775                                if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) {
    782776                                        // expand ttype parameter into its actual type
    783777                                        if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) {
     
    790784
    791785                /// returns flattened version of `src`
    792                 static std::vector< ast::ptr< ast::DeclWithType > > flattenList(
    793                         const std::vector< ast::ptr< ast::DeclWithType > > & src, ast::TypeEnvironment & env
     786                static std::vector< ast::ptr< ast::Type > > flattenList(
     787                        const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env
    794788                ) {
    795                         std::vector< ast::ptr< ast::DeclWithType > > dst;
     789                        std::vector< ast::ptr< ast::Type > > dst;
    796790                        dst.reserve( src.size() );
    797                         for ( const ast::DeclWithType * d : src ) {
     791                        for ( const auto & d : src ) {
    798792                                ast::Pass<TtypeExpander_new> expander{ env };
    799                                 d = d->accept( expander );
    800                                 auto types = flatten( d->get_type() );
     793                                // TtypeExpander pass is impure (may mutate nodes in place)
     794                                // need to make nodes shared to prevent accidental mutation
     795                                ast::ptr<ast::Type> dc = d->accept(expander);
     796                                auto types = flatten( dc );
    801797                                for ( ast::ptr< ast::Type > & t : types ) {
    802798                                        // outermost const, volatile, _Atomic qualifiers in parameters should not play
     
    807803                                        // requirements than a non-mutex function
    808804                                        remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );
    809                                         dst.emplace_back( new ast::ObjectDecl{ d->location, "", t } );
     805                                        dst.emplace_back( t );
    810806                                }
    811807                        }
     
    815811                /// Creates a tuple type based on a list of DeclWithType
    816812                template< typename Iter >
    817                 static ast::ptr< ast::Type > tupleFromDecls( Iter crnt, Iter end ) {
     813                static const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {
    818814                        std::vector< ast::ptr< ast::Type > > types;
    819815                        while ( crnt != end ) {
    820816                                // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
    821817                                // that this results in a flat tuple
    822                                 flatten( (*crnt)->get_type(), types );
     818                                flatten( *crnt, types );
    823819
    824820                                ++crnt;
    825821                        }
    826822
    827                         return { new ast::TupleType{ std::move(types) } };
     823                        return new ast::TupleType{ std::move(types) };
    828824                }
    829825
    830826                template< typename Iter >
    831                 static bool unifyDeclList(
     827                static bool unifyTypeList(
    832828                        Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env,
    833829                        ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
     
    835831                ) {
    836832                        while ( crnt1 != end1 && crnt2 != end2 ) {
    837                                 const ast::Type * t1 = (*crnt1)->get_type();
    838                                 const ast::Type * t2 = (*crnt2)->get_type();
     833                                const ast::Type * t1 = *crnt1;
     834                                const ast::Type * t2 = *crnt2;
    839835                                bool isTuple1 = Tuples::isTtype( t1 );
    840836                                bool isTuple2 = Tuples::isTtype( t2 );
     
    844840                                        // combine remainder of list2, then unify
    845841                                        return unifyExact(
    846                                                 t1, tupleFromDecls( crnt2, end2 ), env, need, have, open,
     842                                                t1, tupleFromTypes( crnt2, end2 ), env, need, have, open,
    847843                                                noWiden(), symtab );
    848844                                } else if ( ! isTuple1 && isTuple2 ) {
    849845                                        // combine remainder of list1, then unify
    850846                                        return unifyExact(
    851                                                 tupleFromDecls( crnt1, end1 ), t2, env, need, have, open,
     847                                                tupleFromTypes( crnt1, end1 ), t2, env, need, have, open,
    852848                                                noWiden(), symtab );
    853849                                }
     
    864860                        if ( crnt1 != end1 ) {
    865861                                // try unifying empty tuple with ttype
    866                                 const ast::Type * t1 = (*crnt1)->get_type();
     862                                const ast::Type * t1 = *crnt1;
    867863                                if ( ! Tuples::isTtype( t1 ) ) return false;
    868864                                return unifyExact(
    869                                         t1, tupleFromDecls( crnt2, end2 ), env, need, have, open,
     865                                        t1, tupleFromTypes( crnt2, end2 ), env, need, have, open,
    870866                                        noWiden(), symtab );
    871867                        } else if ( crnt2 != end2 ) {
    872868                                // try unifying empty tuple with ttype
    873                                 const ast::Type * t2 = (*crnt2)->get_type();
     869                                const ast::Type * t2 = *crnt2;
    874870                                if ( ! Tuples::isTtype( t2 ) ) return false;
    875871                                return unifyExact(
    876                                         tupleFromDecls( crnt1, end1 ), t2, env, need, have, open,
     872                                        tupleFromTypes( crnt1, end1 ), t2, env, need, have, open,
    877873                                        noWiden(), symtab );
    878874                        }
     
    881877                }
    882878
    883                 static bool unifyDeclList(
    884                         const std::vector< ast::ptr< ast::DeclWithType > > & list1,
    885                         const std::vector< ast::ptr< ast::DeclWithType > > & list2,
     879                static bool unifyTypeList(
     880                        const std::vector< ast::ptr< ast::Type > > & list1,
     881                        const std::vector< ast::ptr< ast::Type > > & list2,
    886882                        ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
    887883                        const ast::OpenVarSet & open, const ast::SymbolTable & symtab
    888884                ) {
    889                         return unifyDeclList(
     885                        return unifyTypeList(
    890886                                list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open,
    891887                                symtab );
    892888                }
    893889
    894                 static void markAssertionSet( ast::AssertionSet & assns, const ast::DeclWithType * assn ) {
     890                static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) {
    895891                        auto i = assns.find( assn );
    896892                        if ( i != assns.end() ) {
     
    902898                static void markAssertions(
    903899                        ast::AssertionSet & assn1, ast::AssertionSet & assn2,
    904                         const ast::ParameterizedType * type
     900                        const ast::FunctionType * type
    905901                ) {
    906                         for ( const auto & tyvar : type->forall ) {
    907                                 for ( const ast::DeclWithType * assert : tyvar->assertions ) {
    908                                         markAssertionSet( assn1, assert );
    909                                         markAssertionSet( assn2, assert );
    910                                 }
     902                        for ( auto & assert : type->assertions ) {
     903                                markAssertionSet( assn1, assert );
     904                                markAssertionSet( assn2, assert );
    911905                        }
    912906                }
     
    932926                        ) return;
    933927
    934                         if ( ! unifyDeclList( params, params2, tenv, need, have, open, symtab ) ) return;
    935                         if ( ! unifyDeclList(
     928                        if ( ! unifyTypeList( params, params2, tenv, need, have, open, symtab ) ) return;
     929                        if ( ! unifyTypeList(
    936930                                func->returns, func2->returns, tenv, need, have, open, symtab ) ) return;
    937931
     
    943937
    944938        private:
    945                 template< typename RefType >
    946                 const RefType * handleRefType( const RefType * inst, const ast::Type * other ) {
     939                // Returns: other, cast as XInstType
     940                // Assigns this->result: whether types are compatible (up to generic parameters)
     941                template< typename XInstType >
     942                const XInstType * handleRefType( const XInstType * inst, const ast::Type * other ) {
    947943                        // check that the other type is compatible and named the same
    948                         auto otherInst = dynamic_cast< const RefType * >( other );
    949                         result = otherInst && inst->name == otherInst->name;
     944                        auto otherInst = dynamic_cast< const XInstType * >( other );
     945                        this->result = otherInst && inst->name == otherInst->name;
    950946                        return otherInst;
    951947                }
     
    968964                }
    969965
    970                 template< typename RefType >
    971                 void handleGenericRefType( const RefType * inst, const ast::Type * other ) {
     966                template< typename XInstType >
     967                void handleGenericRefType( const XInstType * inst, const ast::Type * other ) {
    972968                        // check that other type is compatible and named the same
    973                         const RefType * inst2 = handleRefType( inst, other );
    974                         if ( ! inst2 ) return;
     969                        const XInstType * otherInst = handleRefType( inst, other );
     970                        if ( ! this->result ) return;
    975971
    976972                        // check that parameters of types unify, if any
    977973                        const std::vector< ast::ptr< ast::Expr > > & params = inst->params;
    978                         const std::vector< ast::ptr< ast::Expr > > & params2 = inst2->params;
     974                        const std::vector< ast::ptr< ast::Expr > > & params2 = otherInst->params;
    979975
    980976                        auto it = params.begin();
     
    10321028
    10331029                void postvisit( const ast::TypeInstType * typeInst ) {
    1034                         assert( open.find( typeInst->name ) == open.end() );
     1030                        assert( open.find( *typeInst ) == open.end() );
    10351031                        handleRefType( typeInst, type2 );
    10361032                }
     
    10381034        private:
    10391035                /// Creates a tuple type based on a list of Type
    1040                 static ast::ptr< ast::Type > tupleFromTypes(
     1036                static const ast::Type * tupleFromTypes(
    10411037                        const std::vector< ast::ptr< ast::Type > > & tys
    10421038                ) {
     
    11141110
    11151111                        ast::Pass<TtypeExpander_new> expander{ tenv };
     1112
    11161113                        const ast::Type * flat = tuple->accept( expander );
    11171114                        const ast::Type * flat2 = tuple2->accept( expander );
     
    11401137        };
    11411138
     1139        // size_t Unify_new::traceId = Stats::Heap::new_stacktrace_id("Unify_new");
    11421140        bool unify(
    11431141                        const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
     
    11711169                auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 );
    11721170                ast::OpenVarSet::const_iterator
    1173                         entry1 = var1 ? open.find( var1->name ) : open.end(),
    1174                         entry2 = var2 ? open.find( var2->name ) : open.end();
     1171                        entry1 = var1 ? open.find( *var1 ) : open.end(),
     1172                        entry2 = var2 ? open.find( *var2 ) : open.end();
    11751173                bool isopen1 = entry1 != open.end();
    11761174                bool isopen2 = entry2 != open.end();
     
    11881186                        ast::Pass<Unify_new> comparator{ type2, env, need, have, open, widen, symtab };
    11891187                        type1->accept( comparator );
    1190                         return comparator.pass.result;
     1188                        return comparator.core.result;
    11911189                }
    11921190        }
     
    12021200                // force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and
    12031201                // type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1
    1204                 ast::ptr<ast::Type> t1{ type1 }, t2{ type2 };
    1205                 reset_qualifiers( t1 );
    1206                 reset_qualifiers( t2 );
     1202                ast::Type * t1 = shallowCopy(type1.get());
     1203                ast::Type * t2 = shallowCopy(type2.get());
     1204                t1->qualifiers = {};
     1205                t2->qualifiers = {};
     1206                ast::ptr< ast::Type > t1_(t1);
     1207                ast::ptr< ast::Type > t2_(t2);
    12071208
    12081209                if ( unifyExact( t1, t2, env, need, have, open, widen, symtab ) ) {
    1209                         t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones
    1210 
    12111210                        // if exact unification on unqualified types, try to merge qualifiers
    12121211                        if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) {
    1213                                 common = type1;
    1214                                 reset_qualifiers( common, q1 | q2 );
     1212                                t1->qualifiers = q1 | q2;
     1213                                common = t1;
    12151214                                return true;
    12161215                        } else {
     
    12191218
    12201219                } else if (( common = commonType( t1, t2, widen, symtab, env, open ) )) {
    1221                         t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones
    1222 
    12231220                        // no exact unification, but common type
    1224                         reset_qualifiers( common, q1 | q2 );
     1221                        auto c = shallowCopy(common.get());
     1222                        c->qualifiers = q1 | q2;
     1223                        common = c;
    12251224                        return true;
    12261225                } else {
     
    12311230        ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func ) {
    12321231                if ( func->returns.empty() ) return new ast::VoidType{};
    1233                 if ( func->returns.size() == 1 ) return func->returns[0]->get_type();
     1232                if ( func->returns.size() == 1 ) return func->returns[0];
    12341233
    12351234                std::vector<ast::ptr<ast::Type>> tys;
    1236                 for ( const ast::DeclWithType * decl : func->returns ) {
    1237                         tys.emplace_back( decl->get_type() );
     1235                for ( const auto & decl : func->returns ) {
     1236                        tys.emplace_back( decl );
    12381237                }
    12391238                return new ast::TupleType{ std::move(tys) };
  • src/ResolvExpr/module.mk

    r3c64c668 r58fe85a  
    1919      ResolvExpr/Alternative.cc \
    2020      ResolvExpr/AlternativeFinder.cc \
     21      ResolvExpr/AlternativeFinder.h \
     22      ResolvExpr/Alternative.h \
    2123      ResolvExpr/Candidate.cpp \
    2224      ResolvExpr/CandidateFinder.cpp \
     25      ResolvExpr/CandidateFinder.hpp \
     26      ResolvExpr/Candidate.hpp \
    2327      ResolvExpr/CastCost.cc \
    2428      ResolvExpr/CommonType.cc \
    2529      ResolvExpr/ConversionCost.cc \
     30      ResolvExpr/ConversionCost.h \
     31      ResolvExpr/Cost.h \
    2632      ResolvExpr/CurrentObject.cc \
     33      ResolvExpr/CurrentObject.h \
    2734      ResolvExpr/ExplodedActual.cc \
     35      ResolvExpr/ExplodedActual.h \
    2836      ResolvExpr/ExplodedArg.cpp \
     37      ResolvExpr/ExplodedArg.hpp \
    2938      ResolvExpr/FindOpenVars.cc \
     39      ResolvExpr/FindOpenVars.h \
    3040      ResolvExpr/Occurs.cc \
    3141      ResolvExpr/PolyCost.cc \
     
    3343      ResolvExpr/PtrsCastable.cc \
    3444      ResolvExpr/RenameVars.cc \
     45      ResolvExpr/RenameVars.h \
    3546      ResolvExpr/ResolveAssertions.cc \
     47      ResolvExpr/ResolveAssertions.h \
    3648      ResolvExpr/Resolver.cc \
     49      ResolvExpr/Resolver.h \
    3750      ResolvExpr/ResolveTypeof.cc \
     51      ResolvExpr/ResolveTypeof.h \
     52      ResolvExpr/ResolvMode.h \
    3853      ResolvExpr/SatisfyAssertions.cpp \
     54      ResolvExpr/SatisfyAssertions.hpp \
    3955      ResolvExpr/SpecCost.cc \
    4056      ResolvExpr/TypeEnvironment.cc \
    41       ResolvExpr/Unify.cc
     57      ResolvExpr/TypeEnvironment.h \
     58      ResolvExpr/typeops.h \
     59      ResolvExpr/Unify.cc \
     60      ResolvExpr/Unify.h \
     61      ResolvExpr/WidenMode.h
    4262
    43 SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc
     63
     64SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc ResolvExpr/AlternativePrinter.h
    4465SRCDEMANGLE += $(SRC_RESOLVEXPR)
  • src/ResolvExpr/typeops.h

    r3c64c668 r58fe85a  
    1010// Created On       : Sun May 17 07:28:22 2015
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Thu Aug  8 16:36:00 2019
    13 // Update Count     : 5
     12// Last Modified On : Tue Oct  1 09:45:00 2019
     13// Update Count     : 6
    1414//
    1515
     
    8383                const SymTab::Indexer & indexer, const TypeEnvironment & env );
    8484        Cost castCost(
    85                 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
    86                 const ast::TypeEnvironment & env );
     85                const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
     86                const ast::SymbolTable & symtab, const ast::TypeEnvironment & env );
    8787
    8888        // in ConversionCost.cc
     
    9090                const SymTab::Indexer & indexer, const TypeEnvironment & env );
    9191        Cost conversionCost(
    92                 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
    93                 const ast::TypeEnvironment & env );
     92                const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
     93                const ast::SymbolTable & symtab, const ast::TypeEnvironment & env );
    9494
    9595        // in AlternativeFinder.cc
  • src/SymTab/Autogen.cc

    r3c64c668 r58fe85a  
    3838#include "SynTree/Type.h"          // for FunctionType, Type, TypeInstType
    3939#include "SynTree/Visitor.h"       // for maybeAccept, Visitor, acceptAll
     40#include "CompilationState.h"
    4041
    4142class Attribute;
     
    233234        }
    234235
     236        // shallow copy the pointer list for return
     237        std::vector<ast::ptr<ast::TypeDecl>> getGenericParams (const ast::Type * t) {
     238                if (auto structInst = dynamic_cast<const ast::StructInstType*>(t)) {
     239                        return structInst->base->params;
     240                }
     241                if (auto unionInst = dynamic_cast<const ast::UnionInstType*>(t)) {
     242                        return unionInst->base->params;
     243                }
     244                return {};
     245        }
     246
    235247        /// given type T, generate type of default ctor/dtor, i.e. function type void (*) (T *)
    236248        FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic ) {
     
    244256                ftype->parameters.push_back( dstParam );
    245257                return ftype;
     258        }
     259
     260        ///
     261        ast::FunctionDecl * genDefaultFunc(const CodeLocation loc, const std::string fname, const ast::Type * paramType, bool maybePolymorphic) {
     262                std::vector<ast::ptr<ast::TypeDecl>> typeParams;
     263                if (maybePolymorphic) typeParams = getGenericParams(paramType);
     264                auto dstParam = new ast::ObjectDecl(loc, "_dst", new ast::ReferenceType(paramType), nullptr, {}, ast::Linkage::Cforall);
     265                return new ast::FunctionDecl(loc, fname, std::move(typeParams), {dstParam}, {}, new ast::CompoundStmt(loc));
    246266        }
    247267
     
    327347        void FuncGenerator::resolve( FunctionDecl * dcl ) {
    328348                try {
    329                         ResolvExpr::resolveDecl( dcl, indexer );
     349                        if (!useNewAST) // attempt to delay resolver call
     350                                ResolvExpr::resolveDecl( dcl, indexer );
    330351                        if ( functionNesting == 0 ) {
    331352                                // forward declare if top-level struct, so that
     
    339360                } catch ( SemanticErrorException & ) {
    340361                        // okay if decl does not resolve - that means the function should not be generated
    341                         delete dcl;
     362                        // delete dcl;
     363                        delete dcl->statements;
     364                        dcl->statements = nullptr;
     365                        dcl->isDeleted = true;
     366                        definitions.push_back( dcl );
     367                        indexer.addId( dcl );
    342368                }
    343369        }
  • src/SymTab/Autogen.h

    r3c64c668 r58fe85a  
    2121
    2222#include "AST/Decl.hpp"
     23#include "AST/Eval.hpp"
    2324#include "AST/Expr.hpp"
    2425#include "AST/Init.hpp"
     
    5455        /// maybePolymorphic is true if the resulting FunctionType is allowed to be polymorphic
    5556        FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic = true );
     57
     58        ast::FunctionDecl * genDefaultFunc(const CodeLocation loc, const std::string fname, const ast::Type * paramType, bool maybePolymorphic = true);
    5659
    5760        /// generate the type of a copy constructor for paramType.
     
    164167                fExpr->args.emplace_back( dstParam );
    165168
    166                 const ast::Stmt * listInit = srcParam.buildListInit( fExpr );
     169                ast::ptr<ast::Stmt> listInit = srcParam.buildListInit( fExpr );
    167170
    168171                // fetch next set of arguments
     
    265268                }
    266269
    267                 ast::ptr< ast::Expr > begin, end, cmp, update;
     270                ast::ptr< ast::Expr > begin, end;
     271                std::string cmp, update;
    268272
    269273                if ( forward ) {
     
    271275                        begin = ast::ConstantExpr::from_int( loc, 0 );
    272276                        end = array->dimension;
    273                         cmp = new ast::NameExpr{ loc, "?<?" };
    274                         update = new ast::NameExpr{ loc, "++?" };
     277                        cmp = "?<?";
     278                        update = "++?";
    275279                } else {
    276280                        // generate: for ( int i = N-1; i >= 0; --i )
    277                         begin = new ast::UntypedExpr{
    278                                 loc, new ast::NameExpr{ loc, "?-?" },
    279                                 { array->dimension, ast::ConstantExpr::from_int( loc, 1 ) } };
     281                        begin = ast::call(
     282                                loc, "?-?", array->dimension, ast::ConstantExpr::from_int( loc, 1 ) );
    280283                        end = ast::ConstantExpr::from_int( loc, 0 );
    281                         cmp = new ast::NameExpr{ loc, "?>=?" };
    282                         update = new ast::NameExpr{ loc, "--?" };
     284                        cmp = "?>=?";
     285                        update = "--?";
    283286                }
    284287
     
    286289                        loc, indexName.newName(), new ast::BasicType{ ast::BasicType::SignedInt },
    287290                        new ast::SingleInit{ loc, begin } };
    288                
    289                 ast::ptr< ast::Expr > cond = new ast::UntypedExpr{
    290                         loc, cmp, { new ast::VariableExpr{ loc, index }, end } };
    291                
    292                 ast::ptr< ast::Expr > inc = new ast::UntypedExpr{
    293                         loc, update, { new ast::VariableExpr{ loc, index } } };
    294                
    295                 ast::ptr< ast::Expr > dstIndex = new ast::UntypedExpr{
    296                         loc, new ast::NameExpr{ loc, "?[?]" },
    297                         { dstParam, new ast::VariableExpr{ loc, index } } };
     291                ast::ptr< ast::Expr > indexVar = new ast::VariableExpr{ loc, index };
     292               
     293                ast::ptr< ast::Expr > cond = ast::call( loc, cmp, indexVar, end );
     294               
     295                ast::ptr< ast::Expr > inc = ast::call( loc, update, indexVar );
     296               
     297                ast::ptr< ast::Expr > dstIndex = ast::call( loc, "?[?]", dstParam, indexVar );
    298298               
    299299                // srcParam must keep track of the array indices to build the source parameter and/or
    300300                // array list initializer
    301                 srcParam.addArrayIndex( new ast::VariableExpr{ loc, index }, array->dimension );
     301                srcParam.addArrayIndex( indexVar, array->dimension );
    302302
    303303                // for stmt's body, eventually containing call
     
    385385                if ( isUnnamedBitfield( obj ) ) return {};
    386386
    387                 ast::ptr< ast::Type > addCast = nullptr;
     387                ast::ptr< ast::Type > addCast;
    388388                if ( (fname == "?{}" || fname == "^?{}") && ( ! obj || ( obj && ! obj->bitfieldWidth ) ) ) {
    389389                        assert( dstParam->result );
  • src/SymTab/FixFunction.cc

    r3c64c668 r58fe85a  
    106106                bool isVoid = false;
    107107
    108                 void premutate( const ast::FunctionDecl * ) { visit_children = false; }
     108                void previsit( const ast::FunctionDecl * ) { visit_children = false; }
    109109
    110                 const ast::DeclWithType * postmutate( const ast::FunctionDecl * func ) {
     110                const ast::DeclWithType * postvisit( const ast::FunctionDecl * func ) {
    111111                        return new ast::ObjectDecl{
    112112                                func->location, func->name, new ast::PointerType{ func->type }, nullptr,
     
    114114                }
    115115
    116                 void premutate( const ast::ArrayType * ) { visit_children = false; }
     116                void previsit( const ast::ArrayType * ) { visit_children = false; }
    117117
    118                 const ast::Type * postmutate( const ast::ArrayType * array ) {
     118                const ast::Type * postvisit( const ast::ArrayType * array ) {
    119119                        return new ast::PointerType{
    120120                                array->base, array->dimension, array->isVarLen, array->isStatic,
     
    122122                }
    123123
    124                 void premutate( const ast::VoidType * ) { isVoid = true; }
     124                void previsit( const ast::VoidType * ) { isVoid = true; }
    125125
    126                 void premutate( const ast::BasicType * ) { visit_children = false; }
    127                 void premutate( const ast::PointerType * ) { visit_children = false; }
    128                 void premutate( const ast::StructInstType * ) { visit_children = false; }
    129                 void premutate( const ast::UnionInstType * ) { visit_children = false; }
    130                 void premutate( const ast::EnumInstType * ) { visit_children = false; }
    131                 void premutate( const ast::TraitInstType * ) { visit_children = false; }
    132                 void premutate( const ast::TypeInstType * ) { visit_children = false; }
    133                 void premutate( const ast::TupleType * ) { visit_children = false; }
    134                 void premutate( const ast::VarArgsType * ) { visit_children = false; }
    135                 void premutate( const ast::ZeroType * ) { visit_children = false; }
    136                 void premutate( const ast::OneType * ) { visit_children = false; }
     126                void previsit( const ast::BasicType * ) { visit_children = false; }
     127                void previsit( const ast::PointerType * ) { visit_children = false; }
     128                void previsit( const ast::StructInstType * ) { visit_children = false; }
     129                void previsit( const ast::UnionInstType * ) { visit_children = false; }
     130                void previsit( const ast::EnumInstType * ) { visit_children = false; }
     131                void previsit( const ast::TraitInstType * ) { visit_children = false; }
     132                void previsit( const ast::TypeInstType * ) { visit_children = false; }
     133                void previsit( const ast::TupleType * ) { visit_children = false; }
     134                void previsit( const ast::VarArgsType * ) { visit_children = false; }
     135                void previsit( const ast::ZeroType * ) { visit_children = false; }
     136                void previsit( const ast::OneType * ) { visit_children = false; }
    137137        };
    138138} // anonymous namespace
     
    141141        ast::Pass< FixFunction_new > fixer;
    142142        dwt = dwt->accept( fixer );
    143         isVoid |= fixer.pass.isVoid;
     143        isVoid |= fixer.core.isVoid;
    144144        return dwt;
    145145}
  • src/SymTab/Mangler.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Sun May 17 21:40:29 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Feb 15 13:55:12 2020
    13 // Update Count     : 33
     12// Last Modified On : Wed Nov 18 12:01:38 2020
     13// Update Count     : 64
    1414//
    1515#include "Mangler.h"
     
    6565                                void postvisit( const QualifiedType * qualType );
    6666
    67                                 std::string get_mangleName() { return mangleName.str(); }
     67                                std::string get_mangleName() { return mangleName; }
    6868                          private:
    69                                 std::ostringstream mangleName;  ///< Mangled name being constructed
     69                                std::string mangleName;         ///< Mangled name being constructed
    7070                                typedef std::map< std::string, std::pair< int, int > > VarMapType;
    7171                                VarMapType varNums;             ///< Map of type variables to indices
     
    127127                                        isTopLevel = false;
    128128                                } // if
    129                                 mangleName << Encoding::manglePrefix;
     129                                mangleName += Encoding::manglePrefix;
    130130                                const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( declaration->get_name() );
    131131                                if ( opInfo ) {
    132                                         mangleName << opInfo->outputName.size() << opInfo->outputName;
     132                                        mangleName += std::to_string( opInfo->outputName.size() ) + opInfo->outputName;
    133133                                } else {
    134                                         mangleName << declaration->name.size() << declaration->name;
     134                                        mangleName += std::to_string( declaration->name.size() ) + declaration->name;
    135135                                } // if
    136136                                maybeAccept( declaration->get_type(), *visitor );
     
    139139                                        // so they need a different name mangling
    140140                                        if ( declaration->get_linkage() == LinkageSpec::AutoGen ) {
    141                                                 mangleName << Encoding::autogen;
     141                                                mangleName += Encoding::autogen;
    142142                                        } else if ( declaration->get_linkage() == LinkageSpec::Intrinsic ) {
    143                                                 mangleName << Encoding::intrinsic;
     143                                                mangleName += Encoding::intrinsic;
    144144                                        } else {
    145145                                                // if we add another kind of overridable function, this has to change
     
    160160                        void Mangler_old::postvisit( const VoidType * voidType ) {
    161161                                printQualifiers( voidType );
    162                                 mangleName << Encoding::void_t;
     162                                mangleName += Encoding::void_t;
    163163                        }
    164164
     
    166166                                printQualifiers( basicType );
    167167                                assertf( basicType->kind < BasicType::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->kind );
    168                                 mangleName << Encoding::basicTypes[ basicType->kind ];
     168                                mangleName += Encoding::basicTypes[ basicType->kind ];
    169169                        }
    170170
     
    172172                                printQualifiers( pointerType );
    173173                                // mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers
    174                                 if ( ! dynamic_cast<FunctionType *>( pointerType->base ) ) mangleName << Encoding::pointer;
     174                                if ( ! dynamic_cast<FunctionType *>( pointerType->base ) ) mangleName += Encoding::pointer;
    175175                                maybeAccept( pointerType->base, *visitor );
    176176                        }
     
    179179                                // TODO: encode dimension
    180180                                printQualifiers( arrayType );
    181                                 mangleName << Encoding::array << "0";
     181                                mangleName += Encoding::array + "0";
    182182                                maybeAccept( arrayType->base, *visitor );
    183183                        }
     
    204204                        void Mangler_old::postvisit( const FunctionType * functionType ) {
    205205                                printQualifiers( functionType );
    206                                 mangleName << Encoding::function;
     206                                mangleName += Encoding::function;
    207207                                // turn on inFunctionType so that printQualifiers does not print most qualifiers for function parameters,
    208208                                // since qualifiers on outermost parameter type do not differentiate function types, e.g.,
     
    211211                                inFunctionType = true;
    212212                                std::list< Type* > returnTypes = getTypes( functionType->returnVals );
    213                                 if (returnTypes.empty()) mangleName << Encoding::void_t;
     213                                if (returnTypes.empty()) mangleName += Encoding::void_t;
    214214                                else acceptAll( returnTypes, *visitor );
    215                                 mangleName << "_";
     215                                mangleName += "_";
    216216                                std::list< Type* > paramTypes = getTypes( functionType->parameters );
    217217                                acceptAll( paramTypes, *visitor );
    218                                 mangleName << "_";
     218                                mangleName += "_";
    219219                        }
    220220
     
    222222                                printQualifiers( refType );
    223223
    224                                 mangleName << prefix << refType->name.length() << refType->name;
     224                                mangleName += prefix + std::to_string( refType->name.length() ) + refType->name;
    225225
    226226                                if ( mangleGenericParams ) {
    227227                                        const std::list< Expression* > & params = refType->parameters;
    228228                                        if ( ! params.empty() ) {
    229                                                 mangleName << "_";
     229                                                mangleName += "_";
    230230                                                for ( const Expression * param : params ) {
    231231                                                        const TypeExpr * paramType = dynamic_cast< const TypeExpr * >( param );
     
    233233                                                        maybeAccept( paramType->type, *visitor );
    234234                                                }
    235                                                 mangleName << "_";
     235                                                mangleName += "_";
    236236                                        }
    237237                                }
     
    262262                                        // are first found and prefixing with the appropriate encoding for the type class.
    263263                                        assertf( varNum->second.second < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", varNum->second.second );
    264                                         mangleName << Encoding::typeVariables[varNum->second.second] << varNum->second.first;
     264                                        mangleName += Encoding::typeVariables[varNum->second.second] + std::to_string( varNum->second.first );
    265265                                } // if
    266266                        }
     
    268268                        void Mangler_old::postvisit( const TraitInstType * inst ) {
    269269                                printQualifiers( inst );
    270                                 mangleName << inst->name.size() << inst->name;
     270                                mangleName += std::to_string( inst->name.size() ) + inst->name;
    271271                        }
    272272
    273273                        void Mangler_old::postvisit( const TupleType * tupleType ) {
    274274                                printQualifiers( tupleType );
    275                                 mangleName << Encoding::tuple << tupleType->types.size();
     275                                mangleName += Encoding::tuple + std::to_string( tupleType->types.size() );
    276276                                acceptAll( tupleType->types, *visitor );
    277277                        }
     
    280280                                printQualifiers( varArgsType );
    281281                                static const std::string vargs = "__builtin_va_list";
    282                                 mangleName << Encoding::type << vargs.size() << vargs;
     282                                mangleName += Encoding::type + std::to_string( vargs.size() ) + vargs;
    283283                        }
    284284
    285285                        void Mangler_old::postvisit( const ZeroType * ) {
    286                                 mangleName << Encoding::zero;
     286                                mangleName += Encoding::zero;
    287287                        }
    288288
    289289                        void Mangler_old::postvisit( const OneType * ) {
    290                                 mangleName << Encoding::one;
     290                                mangleName += Encoding::one;
    291291                        }
    292292
     
    296296                                        // N marks the start of a qualified type
    297297                                        inQualifiedType = true;
    298                                         mangleName << Encoding::qualifiedTypeStart;
     298                                        mangleName += Encoding::qualifiedTypeStart;
    299299                                }
    300300                                maybeAccept( qualType->parent, *visitor );
     
    303303                                        // E marks the end of a qualified type
    304304                                        inQualifiedType = false;
    305                                         mangleName << Encoding::qualifiedTypeEnd;
     305                                        mangleName += Encoding::qualifiedTypeEnd;
    306306                                }
    307307                        }
     
    315315                                assertf(false, "Mangler_old should not visit typedecl: %s", toCString(decl));
    316316                                assertf( decl->kind < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->kind );
    317                                 mangleName << Encoding::typeVariables[ decl->kind ] << ( decl->name.length() ) << decl->name;
     317                                mangleName += Encoding::typeVariables[ decl->kind ] + std::to_string( decl->name.length() ) + decl->name;
    318318                        }
    319319
     
    330330                                        std::list< std::string > assertionNames;
    331331                                        int dcount = 0, fcount = 0, vcount = 0, acount = 0;
    332                                         mangleName << Encoding::forall;
     332                                        mangleName += Encoding::forall;
    333333                                        for ( const TypeDecl * i : type->forall ) {
    334334                                                switch ( i->kind ) {
     
    354354                                                } // for
    355355                                        } // for
    356                                         mangleName << dcount << "_" << fcount << "_" << vcount << "_" << acount << "_";
    357                                         std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) );
    358                                         mangleName << "_";
     356                                        mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_";
     357                                        for(const auto & a : assertionNames) mangleName += a;
     358//                                      std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) );
     359                                        mangleName += "_";
    359360                                } // if
    360361                                if ( ! inFunctionType ) {
    361362                                        // these qualifiers do not distinguish the outermost type of a function parameter
    362363                                        if ( type->get_const() ) {
    363                                                 mangleName << Encoding::qualifiers.at(Type::Const);
     364                                                mangleName += Encoding::qualifiers.at(Type::Const);
    364365                                        } // if
    365366                                        if ( type->get_volatile() ) {
    366                                                 mangleName << Encoding::qualifiers.at(Type::Volatile);
     367                                                mangleName += Encoding::qualifiers.at(Type::Volatile);
    367368                                        } // if
    368369                                        // Removed due to restrict not affecting function compatibility in GCC
    369370                                        // if ( type->get_isRestrict() ) {
    370                                         //      mangleName << "E";
     371                                        //      mangleName += "E";
    371372                                        // } // if
    372373                                        if ( type->get_atomic() ) {
    373                                                 mangleName << Encoding::qualifiers.at(Type::Atomic);
     374                                                mangleName += Encoding::qualifiers.at(Type::Atomic);
    374375                                        } // if
    375376                                }
    376377                                if ( type->get_mutex() ) {
    377                                         mangleName << Encoding::qualifiers.at(Type::Mutex);
     378                                        mangleName += Encoding::qualifiers.at(Type::Mutex);
    378379                                } // if
    379380                                if ( inFunctionType ) {
     
    383384                                }
    384385                        }
    385                 }       // namespace
     386                } // namespace
    386387        } // namespace Mangler
    387388} // namespace SymTab
     
    417418                        void postvisit( const ast::QualifiedType * qualType );
    418419
    419                         std::string get_mangleName() { return mangleName.str(); }
     420                        std::string get_mangleName() { return mangleName; }
    420421                  private:
    421                         std::ostringstream mangleName;  ///< Mangled name being constructed
     422                        std::string mangleName;         ///< Mangled name being constructed
    422423                        typedef std::map< std::string, std::pair< int, int > > VarMapType;
    423424                        VarMapType varNums;             ///< Map of type variables to indices
     
    437438                  private:
    438439                        void mangleDecl( const ast::DeclWithType *declaration );
    439                         void mangleRef( const ast::ReferenceToType *refType, std::string prefix );
     440                        void mangleRef( const ast::BaseInstType *refType, std::string prefix );
    440441
    441442                        void printQualifiers( const ast::Type *type );
     
    447448                ast::Pass<Mangler_new> mangler( mode );
    448449                maybeAccept( decl, mangler );
    449                 return mangler.pass.get_mangleName();
     450                return mangler.core.get_mangleName();
    450451        }
    451452
     
    470471                                isTopLevel = false;
    471472                        } // if
    472                         mangleName << Encoding::manglePrefix;
     473                        mangleName += Encoding::manglePrefix;
    473474                        const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( decl->name );
    474475                        if ( opInfo ) {
    475                                 mangleName << opInfo->outputName.size() << opInfo->outputName;
     476                                mangleName += std::to_string( opInfo->outputName.size() ) + opInfo->outputName;
    476477                        } else {
    477                                 mangleName << decl->name.size() << decl->name;
     478                                mangleName += std::to_string( decl->name.size() ) + decl->name;
    478479                        } // if
    479480                        maybeAccept( decl->get_type(), *visitor );
     
    482483                                // so they need a different name mangling
    483484                                if ( decl->linkage == ast::Linkage::AutoGen ) {
    484                                         mangleName << Encoding::autogen;
     485                                        mangleName += Encoding::autogen;
    485486                                } else if ( decl->linkage == ast::Linkage::Intrinsic ) {
    486                                         mangleName << Encoding::intrinsic;
     487                                        mangleName += Encoding::intrinsic;
    487488                                } else {
    488489                                        // if we add another kind of overridable function, this has to change
     
    503504                void Mangler_new::postvisit( const ast::VoidType * voidType ) {
    504505                        printQualifiers( voidType );
    505                         mangleName << Encoding::void_t;
     506                        mangleName += Encoding::void_t;
    506507                }
    507508
     
    509510                        printQualifiers( basicType );
    510511                        assertf( basicType->kind < ast::BasicType::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->kind );
    511                         mangleName << Encoding::basicTypes[ basicType->kind ];
     512                        mangleName += Encoding::basicTypes[ basicType->kind ];
    512513                }
    513514
     
    515516                        printQualifiers( pointerType );
    516517                        // mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers
    517                         if ( ! pointerType->base.as<ast::FunctionType>() ) mangleName << Encoding::pointer;
     518                        if ( ! pointerType->base.as<ast::FunctionType>() ) mangleName += Encoding::pointer;
    518519                        maybe_accept( pointerType->base.get(), *visitor );
    519520                }
     
    522523                        // TODO: encode dimension
    523524                        printQualifiers( arrayType );
    524                         mangleName << Encoding::array << "0";
     525                        mangleName += Encoding::array + "0";
    525526                        maybeAccept( arrayType->base.get(), *visitor );
    526527                }
     
    545546                void Mangler_new::postvisit( const ast::FunctionType * functionType ) {
    546547                        printQualifiers( functionType );
    547                         mangleName << Encoding::function;
     548                        mangleName += Encoding::function;
    548549                        // turn on inFunctionType so that printQualifiers does not print most qualifiers for function parameters,
    549550                        // since qualifiers on outermost parameter type do not differentiate function types, e.g.,
     
    551552                        GuardValue( inFunctionType );
    552553                        inFunctionType = true;
    553                         std::vector< ast::ptr< ast::Type > > returnTypes = getTypes( functionType->returns );
    554                         if (returnTypes.empty()) mangleName << Encoding::void_t;
    555                         else accept_each( returnTypes, *visitor );
    556                         mangleName << "_";
    557                         std::vector< ast::ptr< ast::Type > > paramTypes = getTypes( functionType->params );
    558                         accept_each( paramTypes, *visitor );
    559                         mangleName << "_";
    560                 }
    561 
    562                 void Mangler_new::mangleRef( const ast::ReferenceToType * refType, std::string prefix ) {
     554                        if (functionType->returns.empty()) mangleName += Encoding::void_t;
     555                        else accept_each( functionType->returns, *visitor );
     556                        mangleName += "_";
     557                        accept_each( functionType->params, *visitor );
     558                        mangleName += "_";
     559                }
     560
     561                void Mangler_new::mangleRef( const ast::BaseInstType * refType, std::string prefix ) {
    563562                        printQualifiers( refType );
    564563
    565                         mangleName << prefix << refType->name.length() << refType->name;
     564                        mangleName += prefix + std::to_string( refType->name.length() ) + refType->name;
    566565
    567566                        if ( mangleGenericParams ) {
    568567                                if ( ! refType->params.empty() ) {
    569                                         mangleName << "_";
     568                                        mangleName += "_";
    570569                                        for ( const ast::Expr * param : refType->params ) {
    571570                                                auto paramType = dynamic_cast< const ast::TypeExpr * >( param );
     
    573572                                                maybeAccept( paramType->type.get(), *visitor );
    574573                                        }
    575                                         mangleName << "_";
     574                                        mangleName += "_";
    576575                                }
    577576                        }
     
    602601                                // are first found and prefixing with the appropriate encoding for the type class.
    603602                                assertf( varNum->second.second < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", varNum->second.second );
    604                                 mangleName << Encoding::typeVariables[varNum->second.second] << varNum->second.first;
     603                                mangleName += Encoding::typeVariables[varNum->second.second] + std::to_string( varNum->second.first );
    605604                        } // if
    606605                }
     
    608607                void Mangler_new::postvisit( const ast::TraitInstType * inst ) {
    609608                        printQualifiers( inst );
    610                         mangleName << inst->name.size() << inst->name;
     609                        mangleName += std::to_string( inst->name.size() ) + inst->name;
    611610                }
    612611
    613612                void Mangler_new::postvisit( const ast::TupleType * tupleType ) {
    614613                        printQualifiers( tupleType );
    615                         mangleName << Encoding::tuple << tupleType->types.size();
     614                        mangleName += Encoding::tuple + std::to_string( tupleType->types.size() );
    616615                        accept_each( tupleType->types, *visitor );
    617616                }
     
    620619                        printQualifiers( varArgsType );
    621620                        static const std::string vargs = "__builtin_va_list";
    622                         mangleName << Encoding::type << vargs.size() << vargs;
     621                        mangleName += Encoding::type + std::to_string( vargs.size() ) + vargs;
    623622                }
    624623
    625624                void Mangler_new::postvisit( const ast::ZeroType * ) {
    626                         mangleName << Encoding::zero;
     625                        mangleName += Encoding::zero;
    627626                }
    628627
    629628                void Mangler_new::postvisit( const ast::OneType * ) {
    630                         mangleName << Encoding::one;
     629                        mangleName += Encoding::one;
    631630                }
    632631
     
    636635                                // N marks the start of a qualified type
    637636                                inQualifiedType = true;
    638                                 mangleName << Encoding::qualifiedTypeStart;
     637                                mangleName += Encoding::qualifiedTypeStart;
    639638                        }
    640639                        maybeAccept( qualType->parent.get(), *visitor );
     
    643642                                // E marks the end of a qualified type
    644643                                inQualifiedType = false;
    645                                 mangleName << Encoding::qualifiedTypeEnd;
     644                                mangleName += Encoding::qualifiedTypeEnd;
    646645                        }
    647646                }
     
    655654                        assertf(false, "Mangler_new should not visit typedecl: %s", toCString(decl));
    656655                        assertf( decl->kind < ast::TypeDecl::Kind::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->kind );
    657                         mangleName << Encoding::typeVariables[ decl->kind ] << ( decl->name.length() ) << decl->name;
     656                        mangleName += Encoding::typeVariables[ decl->kind ] + std::to_string( decl->name.length() ) + decl->name;
    658657                }
    659658
     
    667666                        // skip if not including qualifiers
    668667                        if ( typeMode ) return;
    669                         if ( auto ptype = dynamic_cast< const ast::ParameterizedType * >(type) ) {
     668                        if ( auto ptype = dynamic_cast< const ast::FunctionType * >(type) ) {
    670669                                if ( ! ptype->forall.empty() ) {
    671670                                        std::list< std::string > assertionNames;
    672671                                        int dcount = 0, fcount = 0, vcount = 0, acount = 0;
    673                                         mangleName << Encoding::forall;
    674                                         for ( const ast::TypeDecl * decl : ptype->forall ) {
     672                                        mangleName += Encoding::forall;
     673                                        for ( auto & decl : ptype->forall ) {
    675674                                                switch ( decl->kind ) {
    676675                                                case ast::TypeDecl::Kind::Dtype:
     
    687686                                                } // switch
    688687                                                varNums[ decl->name ] = std::make_pair( nextVarNum, (int)decl->kind );
    689                                                 for ( const ast::DeclWithType * assert : decl->assertions ) {
    690                                                         ast::Pass<Mangler_new> sub_mangler(
    691                                                                 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums );
    692                                                         assert->accept( sub_mangler );
    693                                                         assertionNames.push_back( sub_mangler.pass.get_mangleName() );
    694                                                         acount++;
    695                                                 } // for
    696688                                        } // for
    697                                         mangleName << dcount << "_" << fcount << "_" << vcount << "_" << acount << "_";
    698                                         std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) );
    699                                         mangleName << "_";
     689                                        for ( auto & assert : ptype->assertions ) {
     690                                                ast::Pass<Mangler_new> sub_mangler(
     691                                                        mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums );
     692                                                assert->var->accept( sub_mangler );
     693                                                assertionNames.push_back( sub_mangler.core.get_mangleName() );
     694                                                acount++;
     695                                        } // for
     696                                        mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_";
     697                                        for(const auto & a : assertionNames) mangleName += a;
     698//                                      std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) );
     699                                        mangleName += "_";
    700700                                } // if
    701701                        } // if
     
    703703                                // these qualifiers do not distinguish the outermost type of a function parameter
    704704                                if ( type->is_const() ) {
    705                                         mangleName << Encoding::qualifiers.at(Type::Const);
     705                                        mangleName += Encoding::qualifiers.at(Type::Const);
    706706                                } // if
    707707                                if ( type->is_volatile() ) {
    708                                         mangleName << Encoding::qualifiers.at(Type::Volatile);
     708                                        mangleName += Encoding::qualifiers.at(Type::Volatile);
    709709                                } // if
    710710                                // Removed due to restrict not affecting function compatibility in GCC
    711711                                // if ( type->get_isRestrict() ) {
    712                                 //      mangleName << "E";
     712                                //      mangleName += "E";
    713713                                // } // if
    714714                                if ( type->is_atomic() ) {
    715                                         mangleName << Encoding::qualifiers.at(Type::Atomic);
     715                                        mangleName += Encoding::qualifiers.at(Type::Atomic);
    716716                                } // if
    717717                        }
    718718                        if ( type->is_mutex() ) {
    719                                 mangleName << Encoding::qualifiers.at(Type::Mutex);
     719                                mangleName += Encoding::qualifiers.at(Type::Mutex);
    720720                        } // if
    721721                        if ( inFunctionType ) {
  • src/SymTab/Validate.cc

    r3c64c668 r58fe85a  
    6464#include "Common/UniqueName.h"         // for UniqueName
    6565#include "Common/utility.h"            // for operator+, cloneAll, deleteAll
     66#include "CompilationState.h"          // skip some passes in new-ast build
    6667#include "Concurrency/Keywords.h"      // for applyKeywords
    6768#include "FixFunction.h"               // for FixFunction
     
    270271        };
    271272
    272         struct ArrayLength : public WithIndexer {
     273        struct InitializerLength {
    273274                /// for array types without an explicit length, compute the length and store it so that it
    274275                /// is known to the rest of the phases. For example,
     
    281282
    282283                void previsit( ObjectDecl * objDecl );
     284        };
     285
     286        struct ArrayLength : public WithIndexer {
     287                static void computeLength( std::list< Declaration * > & translationUnit );
     288
    283289                void previsit( ArrayType * arrayType );
    284290        };
     
    368374                                mutateAll( translationUnit, compoundliteral );
    369375                        });
    370                         Stats::Time::TimeBlock("Resolve With Expressions", [&]() {
    371                                 ResolvExpr::resolveWithExprs( translationUnit ); // must happen before FixObjectType because user-code is resolved and may contain with variables
    372                         });
     376                        if (!useNewAST) {
     377                                Stats::Time::TimeBlock("Resolve With Expressions", [&]() {
     378                                        ResolvExpr::resolveWithExprs( translationUnit ); // must happen before FixObjectType because user-code is resolved and may contain with variables
     379                                });
     380                        }
    373381                }
    374382                {
    375383                        Stats::Heap::newPass("validate-F");
    376384                        Stats::Time::BlockGuard guard("validate-F");
    377                         Stats::Time::TimeCall("Fix Object Type",
    378                                 FixObjectType::fix, translationUnit);
    379                         Stats::Time::TimeCall("Array Length",
    380                                 ArrayLength::computeLength, translationUnit);
     385                        if (!useNewAST) {
     386                                Stats::Time::TimeCall("Fix Object Type",
     387                                        FixObjectType::fix, translationUnit);
     388                        }
     389                        Stats::Time::TimeCall("Initializer Length",
     390                                InitializerLength::computeLength, translationUnit);
     391                        if (!useNewAST) {
     392                                Stats::Time::TimeCall("Array Length",
     393                                        ArrayLength::computeLength, translationUnit);
     394                        }
    381395                        Stats::Time::TimeCall("Find Special Declarations",
    382396                                Validate::findSpecialDecls, translationUnit);
    383397                        Stats::Time::TimeCall("Fix Label Address",
    384398                                mutateAll<LabelAddressFixer>, translationUnit, labelAddrFixer);
    385                         Stats::Time::TimeCall("Handle Attributes",
    386                                 Validate::handleAttributes, translationUnit);
     399                        if (!useNewAST) {
     400                                Stats::Time::TimeCall("Handle Attributes",
     401                                        Validate::handleAttributes, translationUnit);
     402                        }
    387403                }
    388404        }
     
    960976        }
    961977
     978        static bool isNonParameterAttribute( Attribute * attr ) {
     979                static const std::vector<std::string> bad_names = {
     980                        "aligned", "__aligned__",
     981                };
     982                for ( auto name : bad_names ) {
     983                        if ( name == attr->name ) {
     984                                return true;
     985                        }
     986                }
     987                return false;
     988        }
     989
    962990        Type * ReplaceTypedef::postmutate( TypeInstType * typeInst ) {
    963991                // instances of typedef types will come here. If it is an instance
     
    968996                        ret->location = typeInst->location;
    969997                        ret->get_qualifiers() |= typeInst->get_qualifiers();
    970                         // attributes are not carried over from typedef to function parameters/return values
    971                         if ( ! inFunctionType ) {
    972                                 ret->attributes.splice( ret->attributes.end(), typeInst->attributes );
    973                         } else {
    974                                 deleteAll( ret->attributes );
    975                                 ret->attributes.clear();
    976                         }
     998                        // GCC ignores certain attributes if they arrive by typedef, this mimics that.
     999                        if ( inFunctionType ) {
     1000                                ret->attributes.remove_if( isNonParameterAttribute );
     1001                        }
     1002                        ret->attributes.splice( ret->attributes.end(), typeInst->attributes );
    9771003                        // place instance parameters on the typedef'd type
    9781004                        if ( ! typeInst->parameters.empty() ) {
     
    13151341        }
    13161342
     1343        void InitializerLength::computeLength( std::list< Declaration * > & translationUnit ) {
     1344                PassVisitor<InitializerLength> len;
     1345                acceptAll( translationUnit, len );
     1346        }
     1347
    13171348        void ArrayLength::computeLength( std::list< Declaration * > & translationUnit ) {
    13181349                PassVisitor<ArrayLength> len;
     
    13201351        }
    13211352
    1322         void ArrayLength::previsit( ObjectDecl * objDecl ) {
     1353        void InitializerLength::previsit( ObjectDecl * objDecl ) {
    13231354                if ( ArrayType * at = dynamic_cast< ArrayType * >( objDecl->type ) ) {
    13241355                        if ( at->dimension ) return;
     
    13741405        /// Replaces enum types by int, and function/array types in function parameter and return
    13751406        /// lists by appropriate pointers
     1407        /*
    13761408        struct EnumAndPointerDecay_new {
    13771409                const ast::EnumDecl * previsit( const ast::EnumDecl * enumDecl ) {
     
    14241456                }
    14251457        };
     1458        */
    14261459
    14271460        /// expand assertions from a trait instance, performing appropriate type variable substitutions
     
    14421475        }
    14431476
     1477        /*
     1478
    14441479        /// Associates forward declarations of aggregates with their definitions
    14451480        class LinkReferenceToTypes_new final
     
    15081543                }
    15091544
    1510                 void checkGenericParameters( const ast::ReferenceToType * inst ) {
     1545                void checkGenericParameters( const ast::BaseInstType * inst ) {
    15111546                        for ( const ast::Expr * param : inst->params ) {
    15121547                                if ( ! dynamic_cast< const ast::TypeExpr * >( param ) ) {
     
    17721807                static const node_t * forallFixer(
    17731808                        const CodeLocation & loc, const node_t * node,
    1774                         ast::ParameterizedType::ForallList parent_t::* forallField
     1809                        ast::FunctionType::ForallList parent_t::* forallField
    17751810                ) {
    17761811                        for ( unsigned i = 0; i < (node->* forallField).size(); ++i ) {
     
    18231858                }
    18241859        };
     1860        */
    18251861} // anonymous namespace
    18261862
     1863/*
    18271864const ast::Type * validateType(
    18281865                const CodeLocation & loc, const ast::Type * type, const ast::SymbolTable & symtab ) {
    1829         ast::Pass< EnumAndPointerDecay_new > epc;
     1866        // ast::Pass< EnumAndPointerDecay_new > epc;
    18301867        ast::Pass< LinkReferenceToTypes_new > lrt{ loc, symtab };
    18311868        ast::Pass< ForallPointerDecay_new > fpd{ loc };
    18321869
    1833         return type->accept( epc )->accept( lrt )->accept( fpd );
     1870        return type->accept( lrt )->accept( fpd );
    18341871}
     1872*/
    18351873
    18361874} // namespace SymTab
  • src/SymTab/module.mk

    r3c64c668 r58fe85a  
    1717SRC_SYMTAB = \
    1818      SymTab/Autogen.cc \
     19      SymTab/Autogen.h \
    1920      SymTab/FixFunction.cc \
     21      SymTab/FixFunction.h \
    2022      SymTab/Indexer.cc \
     23      SymTab/Indexer.h \
    2124      SymTab/Mangler.cc \
    2225      SymTab/ManglerCommon.cc \
    23       SymTab/Validate.cc
     26      SymTab/Mangler.h \
     27      SymTab/Validate.cc \
     28      SymTab/Validate.h
    2429
    2530SRC += $(SRC_SYMTAB)
  • src/SynTree/AggregateDecl.cc

    r3c64c668 r58fe85a  
    2121#include "Common/utility.h"      // for printAll, cloneAll, deleteAll
    2222#include "Declaration.h"         // for AggregateDecl, TypeDecl, Declaration
     23#include "Expression.h"
    2324#include "Initializer.h"
    2425#include "LinkageSpec.h"         // for Spec, linkageName, Cforall
     
    8889const char * StructDecl::typeString() const { return aggrString( kind ); }
    8990
     91StructInstType * StructDecl::makeInst( std::list< Expression * > const & new_parameters ) {
     92        std::list< Expression * > copy_parameters;
     93        cloneAll( new_parameters, copy_parameters );
     94        return makeInst( move( copy( copy_parameters ) ) );
     95}
     96
     97StructInstType * StructDecl::makeInst( std::list< Expression * > && new_parameters ) {
     98        assert( parameters.size() == new_parameters.size() );
     99        StructInstType * type = new StructInstType( noQualifiers, this );
     100        type->parameters = std::move( new_parameters );
     101        return type;
     102}
     103
    90104const char * UnionDecl::typeString() const { return aggrString( Union ); }
    91105
  • src/SynTree/ApplicationExpr.cc

    r3c64c668 r58fe85a  
    3434
    3535ParamEntry::ParamEntry( const ParamEntry &other ) :
    36                 decl( other.decl ), declptr( maybeClone( other.declptr ) ), actualType( maybeClone( other.actualType ) ), formalType( maybeClone( other.formalType ) ), expr( maybeClone( other.expr ) ) {
     36                decl( other.decl ), declptr( other.declptr ), actualType( maybeClone( other.actualType ) ), formalType( maybeClone( other.formalType ) ), expr( maybeClone( other.expr ) ) {
    3737}
    3838
    3939ParamEntry::~ParamEntry() {
    40         delete declptr;
     40        // delete declptr;
    4141        delete actualType;
    4242        delete formalType;
  • src/SynTree/Declaration.h

    r3c64c668 r58fe85a  
    181181  public:
    182182        Type * base;
    183         std::list< TypeDecl * > parameters;
    184183        std::list< DeclarationWithType * > assertions;
    185184
     
    190189        Type * get_base() const { return base; }
    191190        void set_base( Type * newValue ) { base = newValue; }
    192         std::list< TypeDecl* > & get_parameters() { return parameters; }
    193191        std::list< DeclarationWithType * >& get_assertions() { return assertions; }
    194192
     
    302300
    303301        bool is_coroutine() { return kind == Coroutine; }
    304         bool is_monitor() { return kind == Monitor; }
    305         bool is_thread() { return kind == Thread; }
     302        bool is_generator() { return kind == Generator; }
     303        bool is_monitor  () { return kind == Monitor  ; }
     304        bool is_thread   () { return kind == Thread   ; }
     305
     306        // Make a type instance of this declaration.
     307        StructInstType * makeInst( std::list< Expression * > const & parameters );
     308        StructInstType * makeInst( std::list< Expression * > && parameters );
    306309
    307310        virtual StructDecl * clone() const override { return new StructDecl( *this ); }
  • src/SynTree/Expression.cc

    r3c64c668 r58fe85a  
    3030#include "Type.h"                    // for Type, BasicType, Type::Qualifiers
    3131#include "TypeSubstitution.h"        // for TypeSubstitution
     32#include "CompilationState.h"        // for deterministic_output
    3233
    3334#include "GenPoly/Lvalue.h"
     
    7071        printInferParams( inferParams, os, indent+1, 0 );
    7172
     73        if ( result ) {
     74                os << std::endl << indent << "with resolved type:" << std::endl;
     75                os << (indent+1);
     76                result->print( os, indent+1 );
     77        }
     78
    7279        if ( env ) {
    7380                os << std::endl << indent << "... with environment:" << std::endl;
     
    293300}
    294301
    295 KeywordCastExpr::KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target ) : Expression(), arg(arg), target( target ) {
    296 }
    297 
    298 KeywordCastExpr::KeywordCastExpr( const KeywordCastExpr & other ) : Expression( other ), arg( maybeClone( other.arg ) ), target( other.target ) {
    299 }
     302KeywordCastExpr::KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target ) : Expression(), arg(arg), target( target ) {}
     303KeywordCastExpr::KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target, const KeywordCastExpr::Concrete & concrete_target ) : Expression(), arg(arg), target( target ), concrete_target(concrete_target) {}
     304
     305KeywordCastExpr::KeywordCastExpr( const KeywordCastExpr & other ) : Expression( other ), arg( maybeClone( other.arg ) ), target( other.target ) {}
    300306
    301307KeywordCastExpr::~KeywordCastExpr() {
  • src/SynTree/Expression.h

    r3c64c668 r58fe85a  
    163163};
    164164
     165/// VariableExpr represents an expression that simply refers to the value of a named variable.
     166/// Does not take ownership of var.
     167class VariableExpr : public Expression {
     168  public:
     169        DeclarationWithType * var;
     170
     171        VariableExpr();
     172        VariableExpr( DeclarationWithType * var );
     173        VariableExpr( const VariableExpr & other );
     174        virtual ~VariableExpr();
     175
     176        bool get_lvalue() const final;
     177
     178        DeclarationWithType * get_var() const { return var; }
     179        void set_var( DeclarationWithType * newValue ) { var = newValue; }
     180
     181        static VariableExpr * functionPointer( FunctionDecl * decl );
     182
     183        virtual VariableExpr * clone() const override { return new VariableExpr( * this ); }
     184        virtual void accept( Visitor & v ) override { v.visit( this ); }
     185        virtual void accept( Visitor & v ) const override { v.visit( this ); }
     186        virtual Expression * acceptMutator( Mutator & m ) override { return m.mutate( this ); }
     187        virtual void print( std::ostream & os, Indenter indent = {} ) const override;
     188};
     189
    165190// The following classes are used to represent expression types that cannot be converted into
    166191// function-call format.
     
    206231  public:
    207232        Expression * arg;
    208         bool isGenerated = true; // cast generated implicitly by code generation or explicit in program
     233
     234        // Inidicates cast is introduced by the CFA type system.
     235        // true for casts that the resolver introduces to force a return type
     236        // false for casts from user code
     237        // false for casts from desugaring advanced CFA features into simpler CFA
     238        // example
     239        //   int * p;     // declaration
     240        //   (float *) p; // use, with subject cast
     241        // subject cast isGenerated means we are considering an interpretation with a type mismatch
     242        // subject cast not isGenerated means someone in charge wants it that way
     243        bool isGenerated = true;
    209244
    210245        CastExpr( Expression * arg, bool isGenerated = true );
     
    238273
    239274        KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target );
     275        KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target, const Concrete & concrete_target );
    240276        KeywordCastExpr( const KeywordCastExpr & other );
    241277        virtual ~KeywordCastExpr();
     
    312348
    313349        virtual MemberExpr * clone() const override { return new MemberExpr( * this ); }
    314         virtual void accept( Visitor & v ) override { v.visit( this ); }
    315         virtual void accept( Visitor & v ) const override { v.visit( this ); }
    316         virtual Expression * acceptMutator( Mutator & m ) override { return m.mutate( this ); }
    317         virtual void print( std::ostream & os, Indenter indent = {} ) const override;
    318 };
    319 
    320 /// VariableExpr represents an expression that simply refers to the value of a named variable.
    321 /// Does not take ownership of var.
    322 class VariableExpr : public Expression {
    323   public:
    324         DeclarationWithType * var;
    325 
    326         VariableExpr();
    327         VariableExpr( DeclarationWithType * var );
    328         VariableExpr( const VariableExpr & other );
    329         virtual ~VariableExpr();
    330 
    331         bool get_lvalue() const final;
    332 
    333         DeclarationWithType * get_var() const { return var; }
    334         void set_var( DeclarationWithType * newValue ) { var = newValue; }
    335 
    336         static VariableExpr * functionPointer( FunctionDecl * decl );
    337 
    338         virtual VariableExpr * clone() const override { return new VariableExpr( * this ); }
    339350        virtual void accept( Visitor & v ) override { v.visit( this ); }
    340351        virtual void accept( Visitor & v ) const override { v.visit( this ); }
  • src/SynTree/LinkageSpec.cc

    r3c64c668 r58fe85a  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Sat May 16 13:22:09 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Dec 16 15:02:29 2019
    13 // Update Count     : 28
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Mon Mar  2 16:13:00 2020
     13// Update Count     : 29
    1414//
    1515
     
    2020
    2121#include "LinkageSpec.h"
     22#include "Common/CodeLocation.h"
    2223#include "Common/SemanticError.h"
    2324
  • src/SynTree/LinkageSpec.h

    r3c64c668 r58fe85a  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Sat May 16 13:24:28 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Dec 16 15:03:43 2019
    13 // Update Count     : 20
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Mon Mar  2 16:13:00 2020
     13// Update Count     : 21
    1414//
    1515
     
    1818#include <string>
    1919
    20 #include "Common/CodeLocation.h"
     20struct CodeLocation;
    2121
    2222namespace LinkageSpec {
  • src/SynTree/Mutator.h

    r3c64c668 r58fe85a  
    5151        virtual Statement * mutate( CatchStmt * catchStmt ) = 0;
    5252        virtual Statement * mutate( FinallyStmt * catchStmt ) = 0;
     53        virtual Statement * mutate( SuspendStmt * suspendStmt ) = 0;
    5354        virtual Statement * mutate( WaitForStmt * waitforStmt ) = 0;
    5455        virtual Declaration * mutate( WithStmt * withStmt ) = 0;
  • src/SynTree/NamedTypeDecl.cc

    r3c64c668 r58fe85a  
    2222#include "LinkageSpec.h"         // for Spec, Cforall, linkageName
    2323#include "Type.h"                // for Type, Type::StorageClasses
     24#include "CompilationState.h"
    2425
    2526NamedTypeDecl::NamedTypeDecl( const std::string &name, Type::StorageClasses scs, Type *base )
     
    2829NamedTypeDecl::NamedTypeDecl( const NamedTypeDecl &other )
    2930        : Parent( other ), base( maybeClone( other.base ) ) {
    30         cloneAll( other.parameters, parameters );
    3131        cloneAll( other.assertions, assertions );
    3232}
     
    3434NamedTypeDecl::~NamedTypeDecl() {
    3535        delete base;
    36         deleteAll( parameters );
    3736        deleteAll( assertions );
    3837}
     
    4140        using namespace std;
    4241
    43         if ( name != "" ) os << name << ": ";
     42        if ( ! name.empty() ) {
     43                if( deterministic_output && isUnboundType(name) ) os << "[unbound]:";
     44                else os << name << ": ";
     45        }
    4446
    4547        if ( linkage != LinkageSpec::Cforall ) {
     
    5153                os << " for ";
    5254                base->print( os, indent+1 );
    53         } // if
    54         if ( ! parameters.empty() ) {
    55                 os << endl << indent << "... with parameters" << endl;
    56                 printAll( parameters, os, indent+1 );
    5755        } // if
    5856        if ( ! assertions.empty() ) {
     
    7270                base->print( os, indent+1 );
    7371        } // if
    74         if ( ! parameters.empty() ) {
    75                 os << endl << indent << "... with parameters" << endl;
    76                 printAll( parameters, os, indent+1 );
    77         } // if
    7872}
    7973
  • src/SynTree/ReferenceToType.cc

    r3c64c668 r58fe85a  
    2424#include "Type.h"             // for TypeInstType, StructInstType, UnionInstType
    2525#include "TypeSubstitution.h" // for TypeSubstitution
     26#include "CompilationState.h"
    2627
    2728class Attribute;
     
    205206
    206207        Type::print( os, indent );
    207         os << "instance of " << typeString() << " " << get_name() << " (" << ( isFtype ? "" : "not" ) << " function type)";
     208        os << "instance of " << typeString() << " ";
     209        const auto & name_ = get_name();
     210        if( deterministic_output && isUnboundType(name) ) os << "[unbound]";
     211        else os << name;
     212        os << " (" << ( isFtype ? "" : "not" ) << " function type)";
    208213        if ( ! parameters.empty() ) {
    209214                os << endl << indent << "... with parameters" << endl;
  • src/SynTree/Statement.cc

    r3c64c668 r58fe85a  
    420420}
    421421
     422SuspendStmt::SuspendStmt( const SuspendStmt & other )
     423        : Statement( other )
     424        , then( maybeClone(other.then) )
     425{}
     426
     427SuspendStmt::~SuspendStmt() {
     428        delete then;
     429}
     430
     431void SuspendStmt::print( std::ostream & os, Indenter indent ) const {
     432        os << "Suspend Statement";
     433        switch (type) {
     434                case None     : os << " with implicit target"; break;
     435                case Generator: os << " for generator"       ; break;
     436                case Coroutine: os << " for coroutine"       ; break;
     437        }
     438        os << endl;
     439        indent += 1;
     440
     441        if(then) {
     442                os << indent << " with post statement :" << endl;
     443                then->print( os, indent + 1);
     444        }
     445}
     446
    422447WaitForStmt::WaitForStmt() : Statement() {
    423448        timeout.time      = nullptr;
  • src/SynTree/Statement.h

    r3c64c668 r58fe85a  
    422422};
    423423
     424class SuspendStmt : public Statement {
     425  public:
     426        CompoundStmt * then = nullptr;
     427        enum Type { None, Coroutine, Generator } type = None;
     428
     429        SuspendStmt() = default;
     430        SuspendStmt( const SuspendStmt & );
     431        virtual ~SuspendStmt();
     432
     433        virtual SuspendStmt * clone() const override { return new SuspendStmt( *this ); }
     434        virtual void accept( Visitor & v ) override { v.visit( this ); }
     435        virtual void accept( Visitor & v ) const override { v.visit( this ); }
     436        virtual Statement * acceptMutator( Mutator & m )  override { return m.mutate( this ); }
     437        virtual void print( std::ostream & os, Indenter indent = {} ) const override;
     438};
     439
    424440class WaitForStmt : public Statement {
    425441  public:
     
    502518class ImplicitCtorDtorStmt : public Statement {
    503519  public:
    504         // Non-owned pointer to the constructor/destructor statement
     520        // the constructor/destructor call statement; owned here for a while, eventually transferred elsewhere
    505521        Statement * callStmt;
    506522
  • src/SynTree/SynTree.h

    r3c64c668 r58fe85a  
    5454class CatchStmt;
    5555class FinallyStmt;
     56class SuspendStmt;
    5657class WaitForStmt;
    5758class WithStmt;
  • src/SynTree/Type.cc

    r3c64c668 r58fe85a  
    156156const Type::Qualifiers noQualifiers;
    157157
     158bool isUnboundType(const Type * type) {
     159        if (auto typeInst = dynamic_cast<const TypeInstType *>(type)) {
     160                // xxx - look for a type name produced by renameTyVars.
     161
     162                // TODO: once TypeInstType representation is updated, it should properly check
     163                // if the context id is filled. this is a temporary hack for now
     164                return isUnboundType(typeInst->name);
     165        }
     166        return false;
     167}
     168
     169bool isUnboundType(const std::string & tname) {
     170        // xxx - look for a type name produced by renameTyVars.
     171
     172        // TODO: once TypeInstType representation is updated, it should properly check
     173        // if the context id is filled. this is a temporary hack for now
     174        if (std::count(tname.begin(), tname.end(), '_') >= 3) {
     175                return true;
     176        }
     177        return false;
     178}
     179
    158180// Local Variables: //
    159181// tab-width: 4 //
  • src/SynTree/Type.h

    r3c64c668 r58fe85a  
    733733};
    734734
     735
     736bool isUnboundType(const Type * type);
     737bool isUnboundType(const std::string & tname);
     738
    735739// Local Variables: //
    736740// tab-width: 4 //
  • src/SynTree/TypeDecl.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Mon May 18 07:44:20 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Dec 13 15:26:14 2019
    13 // Update Count     : 21
     12// Last Modified On : Thu Oct  8 18:18:55 2020
     13// Update Count     : 22
    1414//
    1515
     
    2121#include "Type.h"            // for Type, Type::StorageClasses
    2222
    23 TypeDecl::TypeDecl( const std::string & name, Type::StorageClasses scs, Type * type, Kind kind, bool sized, Type * init ) : Parent( name, scs, type ), kind( kind ), sized( kind == Ttype || sized ), init( init ) {
     23TypeDecl::TypeDecl( const std::string & name, Type::StorageClasses scs, Type * type, Kind kind, bool sized, Type * init ) :
     24        Parent( name, scs, type ), kind( kind ), sized( kind == Ttype || sized ), init( init ) {
    2425}
    2526
  • src/SynTree/Visitor.h

    r3c64c668 r58fe85a  
    7878        virtual void visit( FinallyStmt * node ) { visit( const_cast<const FinallyStmt *>(node) ); }
    7979        virtual void visit( const FinallyStmt * finallyStmt ) = 0;
     80        virtual void visit( SuspendStmt * node ) { visit( const_cast<const SuspendStmt *>(node) ); }
     81        virtual void visit( const SuspendStmt * suspendStmt ) = 0;
    8082        virtual void visit( WaitForStmt * node ) { visit( const_cast<const WaitForStmt *>(node) ); }
    8183        virtual void visit( const WaitForStmt * waitforStmt ) = 0;
  • src/SynTree/module.mk

    r3c64c668 r58fe85a  
    2020      SynTree/ApplicationExpr.cc \
    2121      SynTree/ArrayType.cc \
     22      SynTree/Attribute.cc \
     23      SynTree/Attribute.h \
    2224      SynTree/AttrType.cc \
    23       SynTree/Attribute.cc \
     25      SynTree/BaseSyntaxNode.h \
    2426      SynTree/BasicType.cc \
    2527      SynTree/CommaExpr.cc \
    2628      SynTree/CompoundStmt.cc \
    2729      SynTree/Constant.cc \
     30      SynTree/Constant.h \
     31      SynTree/Declaration.cc \
     32      SynTree/Declaration.h \
     33      SynTree/DeclarationWithType.cc \
    2834      SynTree/DeclReplacer.cc \
     35      SynTree/DeclReplacer.h \
    2936      SynTree/DeclStmt.cc \
    30       SynTree/Declaration.cc \
    31       SynTree/DeclarationWithType.cc \
    3237      SynTree/Expression.cc \
     38      SynTree/Expression.h \
    3339      SynTree/FunctionDecl.cc \
    3440      SynTree/FunctionType.cc \
    3541      SynTree/Initializer.cc \
     42      SynTree/Initializer.h \
     43      SynTree/Label.h \
    3644      SynTree/LinkageSpec.cc \
     45      SynTree/LinkageSpec.h \
     46      SynTree/Mutator.h \
    3747      SynTree/NamedTypeDecl.cc \
    3848      SynTree/ObjectDecl.cc \
     
    4151      SynTree/ReferenceType.cc \
    4252      SynTree/Statement.cc \
     53      SynTree/Statement.h \
     54      SynTree/SynTree.h \
    4355      SynTree/TupleExpr.cc \
    4456      SynTree/TupleType.cc \
     
    4658      SynTree/TypeDecl.cc \
    4759      SynTree/TypeExpr.cc \
     60      SynTree/Type.h \
     61      SynTree/TypeofType.cc \
    4862      SynTree/TypeSubstitution.cc \
    49       SynTree/TypeofType.cc \
     63      SynTree/TypeSubstitution.h \
    5064      SynTree/VarArgsType.cc \
     65      SynTree/Visitor.h \
    5166      SynTree/VoidType.cc \
    5267      SynTree/ZeroOneType.cc
  • src/Tuples/Explode.cc

    r3c64c668 r58fe85a  
    129129                        for ( const ast::Expr * expr : tupleExpr->exprs ) {
    130130                                exprs.emplace_back( applyCast( expr, false ) );
    131                                 //exprs.emplace_back( ast::ptr< ast::Expr >( applyCast( expr, false ) ) );
    132131                        }
    133132                        if ( first ) {
     
    148147        }
    149148
    150         const ast::Expr * postmutate( const ast::UniqueExpr * node ) {
     149        const ast::Expr * postvisit( const ast::UniqueExpr * node ) {
    151150                // move cast into unique expr so that the unique expr has type T& rather than
    152151                // type T. In particular, this transformation helps with generating the
     
    162161                        castAdded = false;
    163162                        const ast::Type * newType = getReferenceBase( newNode->result );
    164                         return new ast::CastExpr{ newNode->location, node, newType };
     163                        return new ast::CastExpr{ newNode->location, newNode, newType };
    165164                }
    166165                return newNode;
    167166        }
    168167
    169         const ast::Expr * postmutate( const ast::TupleIndexExpr * tupleExpr ) {
     168        const ast::Expr * postvisit( const ast::TupleIndexExpr * tupleExpr ) {
    170169                // tuple index expr needs to be rebuilt to ensure that the type of the
    171170                // field is consistent with the type of the tuple expr, since the field
     
    180179        ast::Pass<CastExploderCore> exploder;
    181180        expr = expr->accept( exploder );
    182         if ( ! exploder.pass.foundUniqueExpr ) {
     181        if ( ! exploder.core.foundUniqueExpr ) {
    183182                expr = new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
    184183        }
  • src/Tuples/Explode.h

    r3c64c668 r58fe85a  
    210210                        }
    211211                        // Cast a reference away to a value-type to allow further explosion.
    212                         if ( dynamic_cast< const ast::ReferenceType *>( local->result.get() ) ) {
     212                        if ( local->result.as< ast::ReferenceType >() ) {
    213213                                local = new ast::CastExpr{ local, tupleType };
    214214                        }
     
    220220                                // delete idx;
    221221                        }
    222                         // delete local;
    223222                }
    224223        } else {
  • src/Tuples/TupleAssignment.cc

    r3c64c668 r58fe85a  
    465465                                        // resolve ctor/dtor for the new object
    466466                                        ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
    467                                                         InitTweak::genCtorInit( location, ret ), spotter.crntFinder.symtab );
     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.symtab, 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.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.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.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.symtab, matcher->env };
     680                                ResolvExpr::CandidateFinder finder{ crntFinder.localSyms, matcher->env };
    681681
    682682                                try {
  • src/Tuples/TupleExpansion.cc

    r3c64c668 r58fe85a  
    323323                std::vector<ast::ptr<ast::Type>> types;
    324324                ast::CV::Qualifiers quals{
    325                         ast::CV::Const | ast::CV::Volatile | ast::CV::Restrict | ast::CV::Lvalue |
     325                        ast::CV::Const | ast::CV::Volatile | ast::CV::Restrict |
    326326                        ast::CV::Atomic | ast::CV::Mutex };
    327327
  • src/Tuples/Tuples.cc

    r3c64c668 r58fe85a  
    4343        };
    4444        struct ImpurityDetectorIgnoreUnique : public ImpurityDetector {
     45                using ImpurityDetector::previsit;
    4546                void previsit( ast::UniqueExpr const * ) {
    4647                        visit_children = false;
     
    5253                ast::Pass<Detector> detector;
    5354                expr->accept( detector );
    54                 return detector.pass.maybeImpure;
     55                return detector.core.maybeImpure;
    5556        }
    5657} // namespace
  • src/Tuples/module.mk

    r3c64c668 r58fe85a  
    1515###############################################################################
    1616
    17 SRC += Tuples/TupleAssignment.cc Tuples/TupleExpansion.cc Tuples/Explode.cc \
    18         Tuples/Tuples.cc
    19 SRCDEMANGLE += Tuples/TupleAssignment.cc Tuples/TupleExpansion.cc Tuples/Explode.cc \
    20         Tuples/Tuples.cc
     17SRC_TUPLES = \
     18        Tuples/Explode.cc \
     19        Tuples/Explode.h \
     20        Tuples/TupleAssignment.cc \
     21        Tuples/TupleExpansion.cc \
     22        Tuples/Tuples.cc \
     23        Tuples/Tuples.h
     24
     25
     26SRC += $(SRC_TUPLES)
     27SRCDEMANGLE += $(SRC_TUPLES)
  • src/Validate/module.mk

    r3c64c668 r58fe85a  
    1515###############################################################################
    1616
    17 SRC += Validate/HandleAttributes.cc Validate/FindSpecialDecls.cc
    18 SRCDEMANGLE += Validate/HandleAttributes.cc Validate/FindSpecialDecls.cc
     17SRC += Validate/HandleAttributes.cc Validate/HandleAttributes.h Validate/FindSpecialDecls.cc Validate/FindSpecialDecls.h
     18SRCDEMANGLE += Validate/HandleAttributes.cc Validate/HandleAttributes.h Validate/FindSpecialDecls.cc Validate/FindSpecialDecls.h
  • src/Virtual/ExpandCasts.cc

    r3c64c668 r58fe85a  
    1010// Created On       : Mon Jul 24 13:59:00 2017
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Tus Aug  2 14:59:00 2017
    13 // Update Count     : 1
     12// Last Modified On : Fri Jul 31 10:29:00 2020
     13// Update Count     : 4
    1414//
    1515
     
    1818#include <cassert>                 // for assert, assertf
    1919#include <iterator>                // for back_inserter, inserter
    20 #include <map>                     // for map, _Rb_tree_iterator, map<>::ite...
    2120#include <string>                  // for string, allocator, operator==, ope...
    22 #include <utility>                 // for pair
    2321
    2422#include "Common/PassVisitor.h"    // for PassVisitor
     23#include "Common/ScopedMap.h"      // for ScopedMap
    2524#include "Common/SemanticError.h"  // for SemanticError
     25#include "SymTab/Mangler.h"        // for mangleType
    2626#include "SynTree/Declaration.h"   // for ObjectDecl, StructDecl, FunctionDecl
    2727#include "SynTree/Expression.h"    // for VirtualCastExpr, CastExpr, Address...
     
    3131
    3232namespace Virtual {
     33
     34        // Indented until the new ast code gets added.
     35
     36        /// Maps virtual table types the instance for that type.
     37        class VirtualTableMap final {
     38                ScopedMap<std::string, ObjectDecl *> vtable_instances;
     39        public:
     40                void enterScope() {
     41                        vtable_instances.beginScope();
     42                }
     43                void leaveScope() {
     44                        vtable_instances.endScope();
     45                }
     46
     47                ObjectDecl * insert( ObjectDecl * vtableDecl ) {
     48                        std::string const & mangledName = SymTab::Mangler::mangleType( vtableDecl->type );
     49                        ObjectDecl *& value = vtable_instances[ mangledName ];
     50                        if ( value ) {
     51                                if ( vtableDecl->storageClasses.is_extern ) {
     52                                        return nullptr;
     53                                } else if ( ! value->storageClasses.is_extern ) {
     54                                        return value;
     55                                }
     56                        }
     57                        value = vtableDecl;
     58                        return nullptr;
     59                }
     60
     61                ObjectDecl * lookup( const Type * vtableType ) {
     62                        std::string const & mangledName = SymTab::Mangler::mangleType( vtableType );
     63                        const auto it = vtable_instances.find( mangledName );
     64                        return ( vtable_instances.end() == it ) ? nullptr : it->second;
     65                }
     66        };
    3367
    3468        /* Currently virtual depends on the rather brittle name matching between
     
    3973         */
    4074
     75        namespace {
     76
    4177        std::string get_vtable_name( std::string const & name ) {
    4278                return name + "_vtable";
     
    5389        std::string get_vtable_inst_name_root( std::string const & name ) {
    5490                return get_vtable_name_root( name.substr(1, name.size() - 10 ) );
    55         }
    56 
    57         bool is_vtable_name( std::string const & name ) {
    58                 return (name.substr( name.size() - 7 ) == "_vtable" );
    5991        }
    6092
     
    6496        }
    6597
     98        } // namespace
     99
    66100        class VirtualCastCore {
    67         std::map<std::string, ObjectDecl *> vtable_instances;
    68         FunctionDecl *vcast_decl;
    69         StructDecl *pvt_decl;
    70 
    71101                Type * pointer_to_pvt(int level_of_indirection) {
    72102                        Type * type = new StructInstType(
     
    80110        public:
    81111                VirtualCastCore() :
    82                         vtable_instances(), vcast_decl( nullptr ), pvt_decl( nullptr )
     112                        indexer(), vcast_decl( nullptr ), pvt_decl( nullptr )
    83113                {}
    84114
     
    88118
    89119                Expression * postmutate( VirtualCastExpr * castExpr );
     120
     121                VirtualTableMap indexer;
     122        private:
     123                FunctionDecl *vcast_decl;
     124                StructDecl *pvt_decl;
    90125        };
    91126
     
    107142        void VirtualCastCore::premutate( ObjectDecl * objectDecl ) {
    108143                if ( is_vtable_inst_name( objectDecl->get_name() ) ) {
    109                         vtable_instances[objectDecl->get_name()] = objectDecl;
    110                 }
    111         }
     144                        if ( ObjectDecl * existing = indexer.insert( objectDecl ) ) {
     145                                std::string msg = "Repeated instance of virtual table, original found at: ";
     146                                msg += existing->location.filename;
     147                                msg += ":" + toString( existing->location.first_line );
     148                                SemanticError( objectDecl->location, msg );
     149                        }
     150                }
     151        }
     152
     153        namespace {
     154
     155        /// Better error locations for generated casts.
     156        CodeLocation castLocation( const VirtualCastExpr * castExpr ) {
     157                if ( castExpr->location.isSet() ) {
     158                        return castExpr->location;
     159                } else if ( castExpr->arg->location.isSet() ) {
     160                        return castExpr->arg->location;
     161                } else if ( castExpr->result->location.isSet() ) {
     162                        return castExpr->result->location;
     163                } else {
     164                        return CodeLocation();
     165                }
     166        }
     167
     168        [[noreturn]] void castError( const VirtualCastExpr * castExpr, std::string const & message ) {
     169                SemanticError( castLocation( castExpr ), message );
     170        }
     171
     172        /// Get the virtual table type used in a virtual cast.
     173        Type * getVirtualTableType( const VirtualCastExpr * castExpr ) {
     174                const Type * objectType;
     175                if ( auto target = dynamic_cast<const PointerType *>( castExpr->result ) ) {
     176                        objectType = target->base;
     177                } else if ( auto target = dynamic_cast<const ReferenceType *>( castExpr->result ) ) {
     178                        objectType = target->base;
     179                } else {
     180                        castError( castExpr, "Virtual cast type must be a pointer or reference type." );
     181                }
     182                assert( objectType );
     183
     184                const StructInstType * structType = dynamic_cast<const StructInstType *>( objectType );
     185                if ( nullptr == structType ) {
     186                        castError( castExpr, "Virtual cast type must refer to a structure type." );
     187                }
     188                const StructDecl * structDecl = structType->baseStruct;
     189                assert( structDecl );
     190
     191                const ObjectDecl * fieldDecl = nullptr;
     192                if ( 0 < structDecl->members.size() ) {
     193                        const Declaration * memberDecl = structDecl->members.front();
     194                        assert( memberDecl );
     195                        fieldDecl = dynamic_cast<const ObjectDecl *>( memberDecl );
     196                        if ( fieldDecl && fieldDecl->name != "virtual_table" ) {
     197                                fieldDecl = nullptr;
     198                        }
     199                }
     200                if ( nullptr == fieldDecl ) {
     201                        castError( castExpr, "Virtual cast type must have a leading virtual_table field." );
     202                }
     203                const PointerType * fieldType = dynamic_cast<const PointerType *>( fieldDecl->type );
     204                if ( nullptr == fieldType ) {
     205                        castError( castExpr, "Virtual cast type virtual_table field is not a pointer." );
     206                }
     207                assert( fieldType->base );
     208                auto virtualStructType = dynamic_cast<const StructInstType *>( fieldType->base );
     209                assert( virtualStructType );
     210
     211                // Here is the type, but if it is polymorphic it will have lost information.
     212                // (Always a clone so that it may always be deleted.)
     213                StructInstType * virtualType = virtualStructType->clone();
     214                if ( ! structType->parameters.empty() ) {
     215                        deleteAll( virtualType->parameters );
     216                        virtualType->parameters.clear();
     217                        cloneAll( structType->parameters, virtualType->parameters );
     218                }
     219                return virtualType;
     220        }
     221
     222        } // namespace
    112223
    113224        Expression * VirtualCastCore::postmutate( VirtualCastExpr * castExpr ) {
    114                 assertf( castExpr->get_result(), "Virtual Cast target not found before expansion." );
     225                assertf( castExpr->result, "Virtual Cast target not found before expansion." );
    115226
    116227                assert( vcast_decl );
    117228                assert( pvt_decl );
    118229
    119                 // May only cast to a pointer or reference type.
    120                 // A earlier validation should give a syntax error, this is
    121                 // just to make sure errors don't creep during translation.
    122                 // Move to helper with more detailed error messages.
    123                 PointerType * target_type =
    124                         dynamic_cast<PointerType *>( castExpr->get_result() );
    125                 assert( target_type );
    126 
    127                 StructInstType * target_struct =
    128                         dynamic_cast<StructInstType *>( target_type->get_base() );
    129                 assert( target_struct );
    130 
    131                 StructDecl * target_decl = target_struct->get_baseStruct();
    132 
    133                 std::map<std::string, ObjectDecl *>::iterator found =
    134                         vtable_instances.find(
    135                                 get_vtable_inst_name( target_decl->get_name() ) );
    136                 if ( vtable_instances.end() == found ) {
    137                         assertf( false, "virtual table instance not found." );
    138                 }
    139                 ObjectDecl * table = found->second;
     230                const Type * vtable_type = getVirtualTableType( castExpr );
     231                ObjectDecl * table = indexer.lookup( vtable_type );
     232                if ( nullptr == table ) {
     233                        SemanticError( castLocation( castExpr ),
     234                                "Could not find virtual table instance." );
     235                }
    140236
    141237                Expression * result = new CastExpr(
    142                         //new ApplicationExpr(
    143                                 //new AddressExpr( new VariableExpr( vcast_decl ) ),
    144                                 //new CastExpr( new VariableExpr( vcast_decl ),
    145                                 //      new PointerType( noQualifiers,
    146                                 //              vcast_decl->get_type()->clone()
    147                                 //              )
    148                                 //      ),
    149238                        new ApplicationExpr( VariableExpr::functionPointer( vcast_decl ), {
    150239                                        new CastExpr(
     
    163252                castExpr->set_result( nullptr );
    164253                delete castExpr;
     254                delete vtable_type;
    165255                return result;
    166256        }
  • src/Virtual/module.mk

    r3c64c668 r58fe85a  
    1515###############################################################################
    1616
    17 SRC += Virtual/ExpandCasts.cc
     17SRC += Virtual/ExpandCasts.cc Virtual/ExpandCasts.h \
     18        Virtual/Tables.cc Virtual/Tables.h
     19
     20SRCDEMANGLE += Virtual/Tables.cc
  • src/config.h.in

    r3c64c668 r58fe85a  
    2727/* Location of cfa install. */
    2828#undef CFA_PREFIX
     29
     30/* Sets whether or not to use the new-ast, this is adefault value and can be
     31   overrided by --old-ast and --new-ast */
     32#undef CFA_USE_NEW_AST
    2933
    3034/* Major.Minor */
  • src/main.cc

    r3c64c668 r58fe85a  
    99// Author           : Peter Buhr and Rob Schluntz
    1010// Created On       : Fri May 15 23:12:02 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Feb  8 08:33:50 2020
    13 // Update Count     : 633
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Mon Dec  7 15:29:00 2020
     13// Update Count     : 639
    1414//
    1515
     
    3131using namespace std;
    3232
    33 
     33#include "AST/Convert.hpp"
    3434#include "CompilationState.h"
    3535#include "../config.h"                      // for CFA_LIBDIR
     
    4040#include "CodeTools/ResolvProtoDump.h"      // for dumpAsResolvProto
    4141#include "CodeTools/TrackLoc.h"             // for fillLocations
     42#include "Common/CodeLocationTools.hpp"     // for forceFillCodeLocations
    4243#include "Common/CompilerError.h"           // for CompilerError
    4344#include "Common/Stats.h"
     
    312313                } // if
    313314
     315                PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) );
    314316                PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) );
    315317                PASS( "Fix Names", CodeGen::fixNames( translationUnit ) );
     
    339341                } // if
    340342
    341                 PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
    342                 if ( exprp ) {
    343                         dump( translationUnit );
    344                         return EXIT_SUCCESS;
    345                 } // if
     343                if( useNewAST ) {
     344                        if (Stats::Counters::enabled) {
     345                                ast::pass_visitor_stats.avg = Stats::Counters::build<Stats::Counters::AverageCounter<double>>("Average Depth - New");
     346                                ast::pass_visitor_stats.max = Stats::Counters::build<Stats::Counters::MaxCounter<double>>("Max depth - New");
     347                        }
     348                        auto transUnit = convert( move( translationUnit ) );
     349                        PASS( "Resolve", ResolvExpr::resolve( transUnit ) );
     350                        if ( exprp ) {
     351                                translationUnit = convert( move( transUnit ) );
     352                                dump( translationUnit );
     353                                return EXIT_SUCCESS;
     354                        } // if
     355
     356                        forceFillCodeLocations( transUnit );
     357
     358                        PASS( "Fix Init", InitTweak::fix(transUnit, buildingLibrary()));
     359                        translationUnit = convert( move( transUnit ) );
     360                } else {
     361                        PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
     362                        if ( exprp ) {
     363                                dump( translationUnit );
     364                                return EXIT_SUCCESS;
     365                        }
     366
     367                        PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) );
     368                }
    346369
    347370                // fix ObjectDecl - replaces ConstructorInit nodes
    348                 PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) );
    349371                if ( ctorinitp ) {
    350372                        dump ( translationUnit );
     
    354376                PASS( "Expand Unique Expr", Tuples::expandUniqueExpr( translationUnit ) ); // xxx - is this the right place for this? want to expand ASAP so tha, sequent passes don't need to worry about double-visiting a unique expr - needs to go after InitTweak::fix so that copy constructed return declarations are reused
    355377
    356                 PASS( "Translate EHM" , ControlStruct::translateEHM( translationUnit ) );
     378                PASS( "Translate Tries" , ControlStruct::translateTries( translationUnit ) );
    357379
    358380                PASS( "Gen Waitfor" , Concurrency::generateWaitFor( translationUnit ) );
     
    443465
    444466
    445 static const char optstring[] = ":c:ghlLmNnpP:S:twW:D:";
     467static const char optstring[] = ":c:ghlLmNnpdOAP:S:twW:D:";
    446468
    447469enum { PreludeDir = 128 };
     
    456478        { "no-prelude", no_argument, nullptr, 'n' },
    457479        { "prototypes", no_argument, nullptr, 'p' },
     480        { "deterministic-out", no_argument, nullptr, 'd' },
     481        { "old-ast", no_argument, nullptr, 'O'},
     482        { "new-ast", no_argument, nullptr, 'A'},
    458483        { "print", required_argument, nullptr, 'P' },
    459484        { "prelude-dir", required_argument, nullptr, PreludeDir },
     
    467492
    468493static const char * description[] = {
    469         "diagnostic color: never, always, or auto.",          // -c
    470         "wait for gdb to attach",                             // -g
    471         "print help message",                                 // -h
    472         "generate libcfa.c",                                  // -l
    473         "generate line marks",                                // -L
    474         "do not replace main",                                // -m
    475         "do not generate line marks",                         // -N
    476         "do not read prelude",                                // -n
     494        "diagnostic color: never, always, or auto.",            // -c
     495        "wait for gdb to attach",                                                       // -g
     496        "print help message",                                                           // -h
     497        "generate libcfa.c",                                                            // -l
     498        "generate line marks",                                                          // -L
     499        "do not replace main",                                                          // -m
     500        "do not generate line marks",                                           // -N
     501        "do not read prelude",                                                          // -n
    477502        "generate prototypes for prelude functions",            // -p
    478         "print",                                              // -P
     503        "only print deterministic output",                  // -d
     504        "Use the old-ast",                                                                      // -O
     505        "Use the new-ast",                                                                      // -A
     506        "print",                                                                                        // -P
    479507        "<directory> prelude directory for debug/nodebug",      // no flag
    480508        "<option-list> enable profiling information:\n          counters,heap,time,all,none", // -S
    481         "building cfa standard lib",                          // -t
    482         "",                                                   // -w
    483         "",                                                   // -W
    484         "",                                                   // -D
     509        "building cfa standard lib",                                            // -t
     510        "",                                                                                                     // -w
     511        "",                                                                                                     // -W
     512        "",                                                                                                     // -D
    485513}; // description
    486514
     
    580608                        genproto = true;
    581609                        break;
     610                  case 'd':                                     // don't print non-deterministic output
     611                        deterministic_output = true;
     612                        break;
     613                  case 'O':                                     // don't print non-deterministic output
     614                        useNewAST = false;
     615                        break;
     616                  case 'A':                                     // don't print non-deterministic output
     617                        useNewAST = true;
     618                        break;
    582619                  case 'P':                                                                             // print options
    583620                        for ( int i = 0;; i += 1 ) {
  • tests/.expect/alloc.txt

    r3c64c668 r58fe85a  
    14140xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede
    1515CFA array alloc, fill 0xef
    16 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef
     160xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef
    1717CFA array alloc, fill from array
    18 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef,
     180xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef,
    1919
    2020C realloc
    21 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef
     210xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef
    2222CFA realloc
    23 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101
     230xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101
    2424
    25 CFA resize array alloc
     25CFA realloc array alloc
    26260xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef
    27 CFA resize array alloc
     27CFA realloc array alloc
    28280xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101
    29 CFA resize array alloc
     29CFA realloc array alloc
    30300xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef
    31 CFA resize array alloc
    32 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede
    33 CFA resize array alloc
     31CFA realloc array alloc, fill
     320xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede
     33CFA realloc array alloc, fill
    34340xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef
    35 CFA resize array alloc, fill
    36 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede
     35CFA realloc array alloc, fill
     360xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede
    3737
    3838C   memalign 42 42.5
  • tests/.expect/array.txt

    r3c64c668 r58fe85a  
     1array.cfa: In function '_X4mainFi___1':
     2array.cfa:55:9: note: #pragma message: Compiled
  • tests/.expect/cast.txt

    r3c64c668 r58fe85a  
     1cast.cfa: In function '_X4mainFi_iPPKc__1':
     2cast.cfa:18:9: note: #pragma message: Compiled
  • tests/.expect/copyfile.txt

    r3c64c668 r58fe85a  
    88//
    99// Author           : Peter A. Buhr
    10 // Created On       : Tue Jul 16 16:47:22 2019
     10// Created On       : Fri Jun 19 13:44:05 2020
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Jul 17 18:04:44 2019
    13 // Update Count     : 26
     12// Last Modified On : Fri Jun 19 17:58:03 2020
     13// Update Count     : 4
    1414//
    1515
    1616#include <fstream.hfa>
    17 #include <stdlib.hfa>                                                                   // new/delete
     17#include <exception.hfa>
    1818
    1919int main( int argc, char * argv[] ) {
    20         ifstream * in  = &stdin;                                                        // default files
    21         ofstream * out = &stdout;
     20        ifstream in  = stdin;                                                           // copy default files
     21        ofstream out = stdout;
     22
    2223        try {
    2324                choose ( argc ) {
    2425                  case 2, 3:
    25                           in = new( (const char *)argv[1] );            // open input file first as output creates file
    26                           if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent
    27                   case 1: ;                                     // use default files
     26                        open( in, argv[1] );                                            // open input file first as output creates file
     27                        if ( argc == 3 ) open( out, argv[2] );          // do not create output unless input opens
     28                  case 1: ;                                                                             // use default files
    2829                  default:
    29                           exit | "Usage [ input-file (default stdin) [ output-file (default stdout) ] ]";
     30                        exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]";
    3031                } // choose
     32        } catch( Open_Failure * ex ; ex->istream == &in ) {
     33                exit | "Unable to open input file" | argv[1];
     34        } catch( Open_Failure * ex ; ex->ostream == &out ) {
     35                close( in );                                                                    // optional
     36                exit | "Unable to open output file" | argv[2];
     37        } // try
    3138
    32                 char ch;
    33                 *out | nlOff;                                                                   // turn off auto newline
    34                 *in  | nlOn;                                                                    // turn on reading newline
     39        out | nlOff;                                                                            // turn off auto newline
     40        in  | nlOn;                                                                                     // turn on reading newline
    3541
    36                 for () {                                                                                // read all characters
    37                         *in | ch;
    38                   if ( eof( *in ) ) break;                                              // eof ?
    39                         *out | ch;
    40                 } // for
    41         } finally {
    42                 if ( in  != &stdin  ) delete( in );                             // close file, do not delete stdin!
    43                 if ( out != &stdout ) delete( out );                    // close file, do not delete stdout!
    44         } // try
     42        char ch;
     43        for () {                                                                                        // read all characters
     44                in | ch;
     45          if ( eof( in ) ) break;                                                       // eof ?
     46                out | ch;
     47        } //for
    4548} // main
    4649
  • tests/.expect/declarationSpecifier.x64.txt

    r3c64c668 r58fe85a  
    11291129static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return _X4mainFi_iPPKc__1((signed int )argc, (const char **)argv); }
    11301130static inline signed int invoke_main(signed int argc, char **argv, char **envp);
     1131signed int _X13cfa_args_argci_1;
     1132char **_X13cfa_args_argvPPc_1;
     1133char **_X13cfa_args_envpPPc_1;
    11311134signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    11321135    __attribute__ ((unused)) signed int _X12_retval_maini_1;
    11331136    {
     1137        ((void)(_X13cfa_args_argci_1=_X4argci_1));
     1138    }
     1139
     1140    {
     1141        ((void)(_X13cfa_args_argvPPc_1=_X4argvPPc_1));
     1142    }
     1143
     1144    {
     1145        ((void)(_X13cfa_args_envpPPc_1=_X4envpPPc_1));
     1146    }
     1147
     1148    {
    11341149        signed int _tmp_cp_ret4;
    11351150        ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */);
  • tests/.expect/declarationSpecifier.x86.txt

    r3c64c668 r58fe85a  
    11291129static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return _X4mainFi_iPPKc__1((signed int )argc, (const char **)argv); }
    11301130static inline signed int invoke_main(signed int argc, char **argv, char **envp);
     1131signed int _X13cfa_args_argci_1;
     1132char **_X13cfa_args_argvPPc_1;
     1133char **_X13cfa_args_envpPPc_1;
    11311134signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    11321135    __attribute__ ((unused)) signed int _X12_retval_maini_1;
    11331136    {
     1137        ((void)(_X13cfa_args_argci_1=_X4argci_1));
     1138    }
     1139
     1140    {
     1141        ((void)(_X13cfa_args_argvPPc_1=_X4argvPPc_1));
     1142    }
     1143
     1144    {
     1145        ((void)(_X13cfa_args_envpPPc_1=_X4envpPPc_1));
     1146    }
     1147
     1148    {
    11341149        signed int _tmp_cp_ret4;
    11351150        ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */);
  • tests/.expect/enum.txt

    r3c64c668 r58fe85a  
     1done
  • tests/.expect/expression.txt

    r3c64c668 r58fe85a  
     1expression.cfa: In function '_X4mainFi___1':
     2expression.cfa:89:9: note: #pragma message: Compiled
  • tests/.expect/forall.txt

    r3c64c668 r58fe85a  
     1forall.cfa: In function '_X4mainFi___1':
     2forall.cfa:218:9: note: #pragma message: Compiled
  • tests/.expect/gccExtensions.x64.txt

    r3c64c668 r58fe85a  
    321321static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return _X4mainFi_iPPKc__1((signed int )argc, (const char **)argv); }
    322322static inline signed int invoke_main(signed int argc, char **argv, char **envp);
     323signed int _X13cfa_args_argci_1;
     324char **_X13cfa_args_argvPPc_1;
     325char **_X13cfa_args_envpPPc_1;
    323326signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    324327    __attribute__ ((unused)) signed int _X12_retval_maini_1;
    325328    {
     329        ((void)(_X13cfa_args_argci_1=_X4argci_1));
     330    }
     331
     332    {
     333        ((void)(_X13cfa_args_argvPPc_1=_X4argvPPc_1));
     334    }
     335
     336    {
     337        ((void)(_X13cfa_args_envpPPc_1=_X4envpPPc_1));
     338    }
     339
     340    {
    326341        signed int _tmp_cp_ret4;
    327342        ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */);
  • tests/.expect/gccExtensions.x86.txt

    r3c64c668 r58fe85a  
    299299static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return _X4mainFi_iPPKc__1((signed int )argc, (const char **)argv); }
    300300static inline signed int invoke_main(signed int argc, char **argv, char **envp);
     301signed int _X13cfa_args_argci_1;
     302char **_X13cfa_args_argvPPc_1;
     303char **_X13cfa_args_envpPPc_1;
    301304signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    302305    __attribute__ ((unused)) signed int _X12_retval_maini_1;
    303306    {
     307        ((void)(_X13cfa_args_argci_1=_X4argci_1));
     308    }
     309
     310    {
     311        ((void)(_X13cfa_args_argvPPc_1=_X4argvPPc_1));
     312    }
     313
     314    {
     315        ((void)(_X13cfa_args_envpPPc_1=_X4envpPPc_1));
     316    }
     317
     318    {
    304319        signed int _tmp_cp_ret4;
    305320        ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */);
  • tests/.expect/heap.txt

    r3c64c668 r58fe85a  
     1done
  • tests/.expect/identFuncDeclarator.txt

    r3c64c668 r58fe85a  
     1identFuncDeclarator.cfa: In function '_X4mainFi___1':
     2identFuncDeclarator.cfa:116:9: note: #pragma message: Compiled
  • tests/.expect/identParamDeclarator.txt

    r3c64c668 r58fe85a  
     1done
  • tests/.expect/labelledExit.txt

    r3c64c668 r58fe85a  
     1labelledExit.cfa: In function '_X4mainFi_iPPKc__1':
     2labelledExit.cfa:183:9: note: #pragma message: Compiled
  • tests/.expect/limits.txt

    r3c64c668 r58fe85a  
     1limits.cfa: In function '_X4mainFi_iPPKc__1':
     2limits.cfa:154:9: note: #pragma message: Compiled
  • tests/.expect/maybe.txt

    r3c64c668 r58fe85a  
     1done
  • tests/.expect/minmax.txt

    r3c64c668 r58fe85a  
    11char                    z a     min a
    2 signed int              4 3     min 3
     2signed int              4 -3    min -3
    33unsigned int            4 3     min 3
    4 signed long int         4 3     min 3
     4signed long int         4 -3    min -3
    55unsigned long int       4 3     min 3
    6 signed long long int    4 3     min 3
     6signed long long int    4 -3    min -3
    77unsigned long long int  4 3     min 3
    88float                   4. 3.1  min 3.1
     
    1111
    1212char                    z a     max z
    13 signed int              4 3     max 4
     13signed int              4 -3    max 4
    1414unsigned int            4 3     max 4
    15 signed long int         4 3     max 4
     15signed long int         4 -3    max 4
    1616unsigned long int       4 3     max 4
    17 signed long long int    4 3     max 4
     17signed long long int    4 -3    max 4
    1818unsigned long long int  4 3     max 4
    1919float                   4. 3.1  max 4.
  • tests/.expect/nested-types.txt

    r3c64c668 r58fe85a  
     1nested-types.cfa: In function '_X4mainFi___1':
     2nested-types.cfa:102:9: note: #pragma message: Compiled
  • tests/.expect/numericConstants.txt

    r3c64c668 r58fe85a  
     1numericConstants.cfa: In function '_X4mainFi___1':
     2numericConstants.cfa:68:9: note: #pragma message: Compiled
  • tests/.expect/operators.txt

    r3c64c668 r58fe85a  
     1done
  • tests/.expect/result.txt

    r3c64c668 r58fe85a  
     1done
  • tests/.expect/stdincludes.txt

    r3c64c668 r58fe85a  
     1stdincludes.cfa: In function '_X4mainFi___1':
     2stdincludes.cfa:52:9: note: #pragma message: Compiled
  • tests/.expect/switch.txt

    r3c64c668 r58fe85a  
     1switch.cfa: In function '_X4mainFi___1':
     2switch.cfa:105:9: note: #pragma message: Compiled
  • tests/.expect/time.txt

    r3c64c668 r58fe85a  
    1110800 2 3.375 12 1.00001
     20.125 0.0333333333333333 3.375 12000. 1000010.
    230 2 3.375
    347 7 7
  • tests/.expect/typedefRedef-ERR1.txt

    r3c64c668 r58fe85a  
    11typedefRedef.cfa:4:1 error: Cannot redefine typedef: Foo
    2 typedefRedef.cfa:60:1 error: Cannot redefine typedef: ARR
     2typedefRedef.cfa:59:1 error: Cannot redefine typedef: ARR
  • tests/.expect/typedefRedef.txt

    r3c64c668 r58fe85a  
     1typedefRedef.cfa: In function '_X4mainFi___1':
     2typedefRedef.cfa:71:9: note: #pragma message: Compiled
  • tests/.expect/typeof.txt

    r3c64c668 r58fe85a  
     1done
  • tests/.expect/variableDeclarator.txt

    r3c64c668 r58fe85a  
     1variableDeclarator.cfa: In function '_X4mainFi_iPPKc__1':
     2variableDeclarator.cfa:182:9: note: #pragma message: Compiled
  • tests/.expect/voidPtr.txt

    r3c64c668 r58fe85a  
     1done
  • tests/.in/copyfile.txt

    r3c64c668 r58fe85a  
    88//
    99// Author           : Peter A. Buhr
    10 // Created On       : Tue Jul 16 16:47:22 2019
     10// Created On       : Fri Jun 19 13:44:05 2020
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Jul 17 18:04:44 2019
    13 // Update Count     : 26
     12// Last Modified On : Fri Jun 19 17:58:03 2020
     13// Update Count     : 4
    1414//
    1515
    1616#include <fstream.hfa>
    17 #include <stdlib.hfa>                                                                   // new/delete
     17#include <exception.hfa>
    1818
    1919int main( int argc, char * argv[] ) {
    20         ifstream * in  = &stdin;                                                        // default files
    21         ofstream * out = &stdout;
     20        ifstream in  = stdin;                                                           // copy default files
     21        ofstream out = stdout;
     22
    2223        try {
    2324                choose ( argc ) {
    2425                  case 2, 3:
    25                           in = new( (const char *)argv[1] );            // open input file first as output creates file
    26                           if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent
    27                   case 1: ;                                     // use default files
     26                        open( in, argv[1] );                                            // open input file first as output creates file
     27                        if ( argc == 3 ) open( out, argv[2] );          // do not create output unless input opens
     28                  case 1: ;                                                                             // use default files
    2829                  default:
    29                           exit | "Usage [ input-file (default stdin) [ output-file (default stdout) ] ]";
     30                        exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]";
    3031                } // choose
     32        } catch( Open_Failure * ex ; ex->istream == &in ) {
     33                exit | "Unable to open input file" | argv[1];
     34        } catch( Open_Failure * ex ; ex->ostream == &out ) {
     35                close( in );                                                                    // optional
     36                exit | "Unable to open output file" | argv[2];
     37        } // try
    3138
    32                 char ch;
    33                 *out | nlOff;                                                                   // turn off auto newline
    34                 *in  | nlOn;                                                                    // turn on reading newline
     39        out | nlOff;                                                                            // turn off auto newline
     40        in  | nlOn;                                                                                     // turn on reading newline
    3541
    36                 for () {                                                                                // read all characters
    37                         *in | ch;
    38                   if ( eof( *in ) ) break;                                              // eof ?
    39                         *out | ch;
    40                 } // for
    41         } finally {
    42                 if ( in  != &stdin  ) delete( in );                             // close file, do not delete stdin!
    43                 if ( out != &stdout ) delete( out );                    // close file, do not delete stdout!
    44         } // try
     42        char ch;
     43        for () {                                                                                        // read all characters
     44                in | ch;
     45          if ( eof( in ) ) break;                                                       // eof ?
     46                out | ch;
     47        } //for
    4548} // main
    4649
  • tests/.in/manipulatorsInput.txt

    r3c64c668 r58fe85a  
    27273.5 3.5 3.456E+23.456E+2 -0x1.2p-3 3.5 0X1.23p3     3.5
    28283.5 3.5 3.456E+23.456E+2 -0x1.2p-3 3.5 0X1.23p3     3.5
     2925 -25 42798
     301402432282 1505850196993244515
     31394749758663249135511342
     3212935154696204706112391834394
     33
     34423859149128410414395372834994551
     35
     36
     3713889016598639747063234935497057631587
     38170141183460469231731687303715884105727
     39340282366920938463463374607431768211455
     40-340282366920938463463374607431768211455
     41340282366920938463463374607431768211455999
     421234567890123456789 -1234567890123456789
  • tests/Makefile.am

    r3c64c668 r58fe85a  
    1111## Created On       : Sun May 31 09:08:15 2015
    1212## Last Modified By : Peter A. Buhr
    13 ## Last Modified On : Tue Nov 20 11:18:51 2018
    14 ## Update Count     : 68
     13## Last Modified On : Fri Oct  9 23:13:07 2020
     14## Update Count     : 86
    1515###############################################################################
    1616
     
    1818ACLOCAL_AMFLAGS  = -I automake
    1919
    20 include $(top_srcdir)/src/cfa.make
     20include $(top_srcdir)/tools/build/cfa.make
     21
     22DEFAULT_INCLUDES = -I${abs_srcdir}
    2123
    2224debug=yes
     
    3638# since automake doesn't have support for CFA we have to
    3739AM_CFLAGS = $(if $(test), 2> $(test), ) \
     40        -fdebug-prefix-map=$(abspath ${abs_srcdir})= \
     41        -fdebug-prefix-map=/tmp= \
     42        -fno-diagnostics-show-caret \
    3843        -g \
    3944        -Wall \
     
    4247        -DIN_DIR="${abs_srcdir}/.in/"
    4348
     49AM_CFAFLAGS = -XCFA --deterministic-out
     50
    4451# get the desired cfa to test
    4552TARGET_CFA = $(if $(filter $(installed),yes), @CFACC_INSTALL@, @CFACC@)
    4653
    4754# adjust CC to current flags
    48 CC = $(if $(DISTCC_CFA_PATH),distcc $(DISTCC_CFA_PATH) ${ARCH_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS})
     55CC = LC_ALL=C $(if $(DISTCC_CFA_PATH),distcc $(DISTCC_CFA_PATH) ${ARCH_FLAGS} ${AST_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS} ${AST_FLAGS})
    4956CFACC = $(CC)
    5057
     
    5360
    5461# adjusted CC but without the actual distcc call
    55 CFACCLOCAL = $(if $(DISTCC_CFA_PATH),$(DISTCC_CFA_PATH) ${ARCH_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS})
     62CFACCLOCAL = $(if $(DISTCC_CFA_PATH),$(DISTCC_CFA_PATH) ${ARCH_FLAGS} ${AST_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS} ${AST_FLAGS})
     63CFACCLINK = $(CFACCLOCAL) -quiet $(if $(test), 2> $(test), ) $($(shell echo "${@}_FLAGSLD" | sed 's/-\|\//_/g'))
    5664
    5765PRETTY_PATH=mkdir -p $(dir $(abspath ${@})) && cd ${srcdir} &&
     
    6068.INTERMEDIATE: .validate .validate.cfa
    6169EXTRA_PROGRAMS = avl_test .dummy_hack # build but do not install
     70EXTRA_DIST = test.py \
     71        pybin/__init__.py \
     72        pybin/print-core.gdb \
     73        pybin/settings.py \
     74        pybin/test_run.py \
     75        pybin/tools.py \
     76        long_tests.hfa \
     77        .in/io.data \
     78        avltree/avl.h \
     79        avltree/avl-private.h \
     80        concurrent/clib.c \
     81        exceptions/with-threads.hfa \
     82        exceptions/except-io.hfa
     83
     84dist-hook:
     85        echo "Gathering test files"
     86        for file in `${TEST_PY} --list-dist`; do \
     87                if test -f ${srcdir}/$${file}; then \
     88                        $(MKDIR_P) $$(dirname ${distdir}/$${file}); \
     89                        cp -df ${srcdir}/$${file} ${distdir}/$${file}; \
     90                fi; \
     91        done
    6292
    6393avl_test_SOURCES = avltree/avl_test.cfa avltree/avl0.cfa avltree/avl1.cfa avltree/avl2.cfa avltree/avl3.cfa avltree/avl4.cfa avltree/avl-private.cfa
    6494# automake doesn't know we still need C/CPP rules so pretend like we have a C program
    65 _dummy_hack_SOURCES = .dummy_hack.c .dummy_hackxx.cpp
     95nodist__dummy_hack_SOURCES = .dummy_hack.c .dummy_hackxx.cpp
    6696
    6797#----------------------------------------------------------------------------------------------------------------
     
    72102        @+${TEST_PY} --debug=${debug} --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} --all # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
    73103
    74 clean-local :
     104mostlyclean-local :
     105        find ${builddir} -not -path './__pycache__/*' -path '*.o' -delete
     106        find ${builddir} -not -path './__pycache__/*' -path '*/.err/*.log' -delete
     107        find ${builddir} -not -path './__pycache__/*' -path '*/.out/*.log' -delete
    75108        rm -f ${EXTRA_PROGRAMS}
     109        rm -rf __pycache__
     110
     111distclean-local :
     112        find ${builddir} -path '*.Po' -delete
    76113
    77114list :
     
    106143% : %.cfa $(CFACCBIN)
    107144        $(CFACOMPILETEST) -c -o $(abspath ${@}).o
    108         $(CFACCLOCAL) $($(shell echo "${@}_FLAGSLD" | sed 's/-\|\//_/g')) $(abspath ${@}).o -o $(abspath ${@})
     145        $(CFACCLINK) ${@}.o -o $(abspath ${@})
     146        rm $(abspath ${@}).o
    109147
    110148# implicit rule for c++ test
     
    125163        $(CFACOMPILETEST) -CFA -XCFA -p -c -fsyntax-only -o $(abspath ${@})
    126164
    127 # Use for tests where the make command is expected to succeed but the expected.txt should be compared to stderr
    128 EXPECT_STDERR = builtins/sync warnings/self-assignment
    129 $(EXPECT_STDERR): % : %.cfa $(CFACCBIN)
    130         $(CFACOMPILETEST) -c -fsyntax-only 2> $(abspath ${@})
    131 
    132165#------------------------------------------------------------------------------
    133166# CUSTOM TARGET
    134167#------------------------------------------------------------------------------
     168# tests that just validate syntax and compiler output should be compared to stderr
     169CFACOMPILE_SYNTAX = $(CFACOMPILETEST) -Wno-unused-variable -Wno-unused-label -c -fsyntax-only -o $(abspath ${@})
     170
     171SYNTAX_ONLY_CODE = expression typedefRedef variableDeclarator switch numericConstants identFuncDeclarator forall \
     172        init1 limits nested-types stdincludes cast labelledExit array builtins/sync warnings/self-assignment
     173$(SYNTAX_ONLY_CODE): % : %.cfa $(CFACCBIN)
     174        $(CFACOMPILE_SYNTAX)
     175        $(if $(test), cp $(test) $(abspath ${@}), )
     176
    135177# expected failures
    136 # use custom target since they require a custom define and custom dependencies
     178# use custom target since they require a custom define *and* have a name that doesn't match the file
    137179alloc-ERROR : alloc.cfa $(CFACCBIN)
    138         $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@})
     180        $(CFACOMPILE_SYNTAX) -DERR1
     181        -cp $(test) $(abspath ${@})
     182
     183init1-ERROR : init1.cfa $(CFACCBIN)
     184        $(CFACOMPILE_SYNTAX) -DERR1
     185        -cp $(test) $(abspath ${@})
    139186
    140187typedefRedef-ERR1 : typedefRedef.cfa $(CFACCBIN)
    141         $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@})
     188        $(CFACOMPILE_SYNTAX) -DERR1
     189        -cp $(test) $(abspath ${@})
    142190
    143191nested-types-ERR1 : nested-types.cfa $(CFACCBIN)
    144         $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@})
     192        $(CFACOMPILE_SYNTAX) -DERR1
     193        -cp $(test) $(abspath ${@})
    145194
    146195nested-types-ERR2 : nested-types.cfa $(CFACCBIN)
    147         $(CFACOMPILETEST) -DERR2 -c -fsyntax-only -o $(abspath ${@})
     196        $(CFACOMPILE_SYNTAX) -DERR2
     197        -cp $(test) $(abspath ${@})
    148198
    149199raii/memberCtors-ERR1 : raii/memberCtors.cfa $(CFACCBIN)
    150         $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@})
     200        $(CFACOMPILE_SYNTAX) -DERR1
     201        -cp $(test) $(abspath ${@})
    151202
    152203raii/ctor-autogen-ERR1 : raii/ctor-autogen.cfa $(CFACCBIN)
    153         $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@})
     204        $(CFACOMPILE_SYNTAX) -DERR1
     205        -cp $(test) $(abspath ${@})
    154206
    155207raii/dtor-early-exit-ERR1 : raii/dtor-early-exit.cfa $(CFACCBIN)
    156         $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@})
     208        $(CFACOMPILE_SYNTAX) -DERR1
     209        -cp $(test) $(abspath ${@})
    157210
    158211raii/dtor-early-exit-ERR2 : raii/dtor-early-exit.cfa $(CFACCBIN)
    159         $(CFACOMPILETEST) -DERR2 -c -fsyntax-only -o $(abspath ${@})
     212        $(CFACOMPILE_SYNTAX) -DERR2
     213        -cp $(test) $(abspath ${@})
     214
     215# Exception Tests
     216# Test with libcfathread; it changes how storage works.
     217
     218exceptions/%-threads : exceptions/%.cfa $(CFACCBIN)
     219        $(CFACOMPILETEST) -include exceptions/with-threads.hfa -c -o $(abspath ${@}).o
     220        $(CFACCLOCAL) $($(shell echo "${@}_FLAGSLD" | sed 's/-\|\//_/g')) $(abspath ${@}).o -o $(abspath ${@})
     221
     222# Linking tests
     223# Meta tests to make sure we see linking errors (can't compile with -O2 since it may multiply number of calls)
     224linking/linkerror : linking/linkerror.cfa $(CFACCBIN)
     225        $(CFACOMPILETEST) -O0 -c -o $(abspath ${@}).o
     226        $(CFACCLINK)  -O0 ${@}.o -o $(abspath ${@})
     227        rm $(abspath ${@}).o
    160228
    161229#------------------------------------------------------------------------------
  • tests/alloc.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed Feb  3 07:56:22 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Feb 16 09:21:13 2020
    13 // Update Count     : 405
     12// Last Modified On : Thu Nov 12 10:02:18 2020
     13// Update Count     : 432
    1414//
    1515
     
    2828        size_t dim = 10;
    2929        char fill = '\xde';
    30         int * p, * p1;
     30        int * ip, * ip1;
    3131
    3232        // allocation, non-array types
    3333
    34         p = (int *)malloc( sizeof(*p) );                                        // C malloc, type unsafe
    35         *p = 0xdeadbeef;
    36         printf( "C   malloc %#x\n", *p );
    37         free( p );
    38 
    39         p = malloc();                                       // CFA malloc, type safe
    40         *p = 0xdeadbeef;
    41         printf( "CFA malloc %#x\n", *p );
    42         free( p );
    43 
    44         p = alloc();                                        // CFA alloc, type safe
    45         *p = 0xdeadbeef;
    46         printf( "CFA alloc %#x\n", *p );
    47         free( p );
    48 
    49         p = alloc_set( fill );                                                          // CFA alloc, fill
    50         printf( "CFA alloc, fill %08x\n", *p );
    51         free( p );
    52 
    53         p = alloc_set( 3 );                                                                     // CFA alloc, fill
    54         printf( "CFA alloc, fill %d\n", *p );
    55         free( p );
     34        ip = (int *)malloc( sizeof(*ip) );                                      // C malloc, type unsafe
     35        *ip = 0xdeadbeef;
     36        printf( "C   malloc %#x\n", *ip );
     37        free( ip );
     38
     39        ip = malloc();                                                                          // CFA malloc, type safe
     40        *ip = 0xdeadbeef;
     41        printf( "CFA malloc %#x\n", *ip );
     42        free( ip );
     43
     44        ip = alloc();                                                                           // CFA alloc, type safe
     45        *ip = 0xdeadbeef;
     46        printf( "CFA alloc %#x\n", *ip );
     47        free( ip );
     48
     49        ip = alloc( fill`fill );                                                                // CFA alloc, fill
     50        printf( "CFA alloc, fill %08x\n", *ip );
     51        free( ip );
     52
     53        ip = alloc( 3`fill );                                                           // CFA alloc, fill
     54        printf( "CFA alloc, fill %d\n", *ip );
     55        free( ip );
    5656
    5757
     
    5959        printf( "\n" );
    6060
    61         p = (int *)calloc( dim, sizeof( *p ) );                         // C array calloc, type unsafe
     61        ip = (int *)calloc( dim, sizeof( *ip ) );                       // C array calloc, type unsafe
    6262        printf( "C   array calloc, fill 0\n" );
    63         for ( i; dim ) { printf( "%#x ", p[i] ); }
    64         printf( "\n" );
    65         free( p );
    66 
    67         p = calloc( dim );                                  // CFA array calloc, type safe
     63        for ( i; dim ) { printf( "%#x ", ip[i] ); }
     64        printf( "\n" );
     65        free( ip );
     66
     67        ip = calloc( dim );                                                                     // CFA array calloc, type safe
    6868        printf( "CFA array calloc, fill 0\n" );
    69         for ( i; dim ) { printf( "%#x ", p[i] ); }
    70         printf( "\n" );
    71         free( p );
    72 
    73         p = alloc( dim );                                   // CFA array alloc, type safe
    74         for ( i; dim ) { p[i] = 0xdeadbeef; }
     69        for ( i; dim ) { printf( "%#x ", ip[i] ); }
     70        printf( "\n" );
     71        free( ip );
     72
     73        ip = alloc( dim );                                                                      // CFA array alloc, type safe
     74        for ( i; dim ) { ip[i] = 0xdeadbeef; }
    7575        printf( "CFA array alloc, no fill\n" );
    76         for ( i; dim ) { printf( "%#x ", p[i] ); }
    77         printf( "\n" );
    78         free( p );
    79 
    80         p = alloc_set( 2 * dim, fill );                                         // CFA array alloc, fill
     76        for ( i; dim ) { printf( "%#x ", ip[i] ); }
     77        printf( "\n" );
     78        free( ip );
     79
     80        ip = alloc( 2 * dim, fill`fill );                                       // CFA array alloc, fill
    8181        printf( "CFA array alloc, fill %#hhx\n", fill );
    82         for ( i; 2 * dim ) { printf( "%#x ", p[i] ); }
    83         printf( "\n" );
    84         free( p );
    85 
    86         p = alloc_set( 2 * dim, 0xdeadbeef );                           // CFA array alloc, fill
     82        for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); }
     83        printf( "\n" );
     84        free( ip );
     85
     86        ip = alloc( 2 * dim, ((int)0xdeadbeef)`fill );                          // CFA array alloc, fill
    8787        printf( "CFA array alloc, fill %#hhx\n", 0xdeadbeef );
    88         for ( i; 2 * dim ) { printf( "%#x ", p[i] ); }
    89         printf( "\n" );
    90         // do not free
    91 
    92         p1 = alloc_set( 2 * dim, p );                                           // CFA array alloc, fill
     88        for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); }
     89        printf( "\n" );
     90        // do not free
     91
     92        ip1 = alloc( 2 * dim, [ip, 2 * dim]`fill );                             // CFA array alloc, fill
    9393        printf( "CFA array alloc, fill from array\n" );
    94         for ( i; 2 * dim ) { printf( "%#x %#x, ", p[i], p1[i] ); }
    95         free( p1 );
    96         printf( "\n" );
    97 
     94        for ( i; 2 * dim ) { printf( "%#x %#x, ", ip[i], ip1[i] ); }
     95        free( ip1 );
     96        printf( "\n" );
     97
     98
     99        // realloc, non-array types
     100        printf( "\n" );
     101
     102        ip = (int *)realloc( ip, dim * sizeof(*ip) );           // C realloc
     103        printf( "C realloc\n" );
     104        for ( i; dim ) { printf( "%#x ", ip[i] ); }
     105        printf( "\n" );
     106        // do not free
     107
     108        ip = realloc( ip, 2 * dim * sizeof(*ip) );                      // CFA realloc
     109        for ( i; dim ~ 2 * dim ) { ip[i] = 0x1010101; }
     110        printf( "CFA realloc\n" );
     111        for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); }
     112        printf( "\n" );
     113        // do not free
     114
     115
     116        // realloc, array types
     117        printf( "\n" );
     118
     119        ip = alloc( dim, ip`realloc );                                                          // CFA realloc array alloc
     120        for ( i; dim ) { ip[i] = 0xdeadbeef; }
     121        printf( "CFA realloc array alloc\n" );
     122        for ( i; dim ) { printf( "%#x ", ip[i] ); }
     123        printf( "\n" );
     124        // do not free
     125
     126        ip = alloc( 2 * dim, ip`realloc );                                                      // CFA realloc array alloc
     127        for ( i; dim ~ 2 * dim ) { ip[i] = 0x1010101; }         // fill upper part
     128        printf( "CFA realloc array alloc\n" );
     129        for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); }
     130        printf( "\n" );
     131        // do not free
     132
     133        ip = alloc( dim, ip`realloc );                                                          // CFA realloc array alloc
     134        printf( "CFA realloc array alloc\n" );
     135        for ( i; dim ) { printf( "%#x ", ip[i] ); }
     136        printf( "\n" );
     137        // do not free
     138
     139        ip = alloc( 3 * dim, ip`realloc, fill`fill );                           // CFA realloc array alloc, fill
     140        printf( "CFA realloc array alloc, fill\n" );
     141        for ( i; 3 * dim ) { printf( "%#x ", ip[i] ); }
     142        printf( "\n" );
     143        // do not free
     144
     145        ip = alloc( dim, ip`realloc, fill`fill );                                       // CFA realloc array alloc, fill
     146        printf( "CFA realloc array alloc, fill\n" );
     147        for ( i; dim ) { printf( "%#x ", ip[i] ); }
     148        printf( "\n" );
     149        // do not free
     150
     151        ip = alloc( 3 * dim, ip`realloc, fill`fill );                           // CFA realloc array alloc, fill
     152        printf( "CFA realloc array alloc, fill\n" );
     153        for ( i; 3 * dim ) { printf( "%#x ", ip[i] ); }
     154        printf( "\n" );
     155        // do not free
     156#if 0 // FIX ME
     157        ip = alloc( 5 * dim, ip`realloc, 5`fill );                                      // CFA realloc array alloc, 5
     158        printf( "CFA realloc array alloc, 5\n" );
     159        for ( i; 5 * dim ) { printf( "%#x ", ip[i] ); }
     160        printf( "\n" );
     161        // do not free
     162
     163        ip = alloc( dim, ip`realloc, 5`fill );                                          // CFA realloc array alloc, 5
     164        printf( "CFA realloc array alloc, 5\n" );
     165        for ( i; dim ) { printf( "%#x ", ip[i] ); }
     166        printf( "\n" );
     167        // do not free
     168
     169        ip = alloc( 5 * dim, ip`realloc, 5`fill );                                      // CFA realloc array alloc, 5
     170        printf( "CFA realloc array alloc, 5\n" );
     171        for ( i; 5 * dim ) { printf( "%#x ", ip[i] ); }
     172        printf( "\n" );
     173#endif // 0
     174        free( ip );
    98175
    99176        // resize, non-array types
    100         printf( "\n" );
    101 
    102         p = (int *)realloc( p, dim * sizeof(*p) );                      // C realloc
    103         printf( "C realloc\n" );
    104         for ( i; dim ) { printf( "%#x ", p[i] ); }
    105         printf( "\n" );
    106         // do not free
    107 
    108         p = realloc( p, 2 * dim * sizeof(*p) );             // CFA realloc
    109         for ( i; dim ~ 2 * dim ) { p[i] = 0x1010101; }
    110         printf( "CFA realloc\n" );
    111         for ( i; 2 * dim ) { printf( "%#x ", p[i] ); }
    112         printf( "\n" );
    113         // do not free
     177
     178        struct S {
     179                int a[5];
     180        };
     181
     182    ip = alloc();
     183        *ip = 5;
     184    double * dp = alloc( ip`resize );
     185        *dp = 5.5;
     186    S * sp = alloc( dp`resize );
     187        *sp = (S){ {0, 1, 2, 3, 4} };
     188    ip = alloc( sp`resize );
     189        *ip = 3;
     190    free( ip );
    114191
    115192
    116193        // resize, array types
    117         printf( "\n" );
    118 
    119         p = alloc( p, dim );                                // CFA resize array alloc
    120         for ( i; dim ) { p[i] = 0xdeadbeef; }
    121         printf( "CFA resize array alloc\n" );
    122         for ( i; dim ) { printf( "%#x ", p[i] ); }
    123         printf( "\n" );
    124         // do not free
    125 
    126         p = alloc( p, 2 * dim );                            // CFA resize array alloc
    127         for ( i; dim ~ 2 * dim ) { p[i] = 0x1010101; }          // fill upper part
    128         printf( "CFA resize array alloc\n" );
    129         for ( i; 2 * dim ) { printf( "%#x ", p[i] ); }
    130         printf( "\n" );
    131         // do not free
    132 
    133         p = alloc( p, dim );                                // CFA resize array alloc
    134         printf( "CFA resize array alloc\n" );
    135         for ( i; dim ) { printf( "%#x ", p[i] ); }
    136         printf( "\n" );
    137         // do not free
    138 
    139         p = alloc_set( p, 3 * dim, fill );                                      // CFA resize array alloc, fill
    140         printf( "CFA resize array alloc\n" );
    141         for ( i; 3 * dim ) { printf( "%#x ", p[i] ); }
    142         printf( "\n" );
    143         // do not free
    144 
    145         p = alloc_set( p, dim, fill );                                          // CFA resize array alloc, fill
    146         printf( "CFA resize array alloc\n" );
    147         for ( i; dim ) { printf( "%#x ", p[i] ); }
    148         printf( "\n" );
    149         // do not free
    150 
    151         p = alloc_set( p, 3 * dim, fill );                                      // CFA resize array alloc, fill
    152         printf( "CFA resize array alloc, fill\n" );
    153         for ( i; 3 * dim ) { printf( "%#x ", p[i] );; }
    154         printf( "\n" );
    155         free( p );
    156 
    157 
     194
     195    ip = alloc( 5 );
     196        for ( i; 5 ) { ip[i] = 5; }
     197    dp = alloc( 5, ip`resize );
     198        for ( i; 5 ) { dp[i] = 5.5; }
     199    sp = alloc( 5, dp`resize );
     200        for ( i; 5 ) { sp[i] = (S){ {0, 1, 2, 3, 4} }; }
     201    ip = alloc( 3, sp`resize );
     202        for ( i; 3 ) { ip[i] = 3; }
     203    ip = alloc( 7, ip`realloc );
     204        for ( i; 7 ) { ip[i] = 7; }
     205    ip = alloc( 7, ip`resize );
     206        for ( i; 7 ) { ip[i] = 7; }
     207    free( ip );
     208
     209
     210        int const_count, dest_count;
    158211        struct Struct { int x; double y; };
     212        void  ?{}( Struct & a ) {                                       // construct
     213                a.[ x, y ] = [ -1, -1.0 ];
     214        }
     215        void  ?{}( Struct & a, int x, double y ) {      // initialize
     216                a.[ x, y ] = [ x, y ];
     217                const_count++;
     218        }
     219        void ^?{}( Struct & a ) {  dest_count++; }      // destruct
    159220        Struct st, st1, sta[dim], sta1[dim], * stp, * stp1;
    160221
     
    168229        free( stp );
    169230
    170         stp = &(*memalign( Alignment )){ 42, 42.5 };          // CFA memalign
     231        stp = &(*memalign( Alignment )){ 42, 42.5 };            // CFA memalign
    171232        assert( (uintptr_t)stp % Alignment == 0 );
    172233        printf( "CFA memalign %d %g\n", stp->x, stp->y );
     
    185246        free( stp );
    186247
    187         stp = &(*alloc_align( Alignment)){ 42, 42.5 };          // CFA alloc_align
     248        stp = &(*alloc( Alignment`align)){ 42, 42.5 };          // CFA alloc_align
    188249        assert( (uintptr_t)stp % Alignment == 0 );
    189250        printf( "CFA alloc_align %d %g\n", stp->x, stp->y );
    190251        free( stp );
    191252
    192         stp = &(*alloc_align( Alignment )){ 42, 42.5 };         // CFA alloc_align
     253        stp = &(*alloc( Alignment`align )){ 42, 42.5 };         // CFA alloc_align
    193254        assert( (uintptr_t)stp % Alignment == 0 );
    194255        printf( "CFA alloc_align %d %g\n", stp->x, stp->y );
    195256        free( stp );
    196257
    197         stp = alloc_align_set( Alignment, fill );                       // CFA memalign, fill
     258        stp = alloc( Alignment`align, fill`fill );                      // CFA memalign, fill
    198259        assert( (uintptr_t)stp % Alignment == 0 );
    199260        printf( "CFA alloc_align fill %#x %a\n", stp->x, stp->y );
    200261        free( stp );
    201262
    202         stp = alloc_align_set( Alignment, (Struct){ 42, 42.5 } ); // CFA memalign, fill
     263        stp = alloc( Alignment`align, (Struct){ 42, 42.5 }`fill ); // CFA memalign, fill
    203264        assert( (uintptr_t)stp % Alignment == 0 );
    204265        printf( "CFA alloc_align fill %d %g\n", stp->x, stp->y );
    205266        // do not free
    206267
    207         stp = &(*alloc_align( stp, 4096 )){ 42, 42.5 };         // CFA realign
     268        stp = &(*alloc( stp`realloc, 4096`align )){ 42, 42.5 };         // CFA realign
    208269        assert( (uintptr_t)stp % 4096 == 0 );
    209270        printf( "CFA alloc_align %d %g\n", stp->x, stp->y );
     
    214275        printf( "\n" );
    215276
    216         stp = alloc_align( Alignment, dim );                // CFA array memalign
     277        stp = alloc( dim, Alignment`align );                // CFA array memalign
    217278        assert( (uintptr_t)stp % Alignment == 0 );
    218279        for ( i; dim ) { stp[i] = (Struct){ 42, 42.5 }; }
     
    222283        free( stp );
    223284
    224         stp = alloc_align_set( Alignment, dim, fill );          // CFA array memalign, fill
     285        stp = alloc( dim, Alignment`align, fill`fill );         // CFA array memalign, fill
    225286        assert( (uintptr_t)stp % Alignment == 0 );
    226287        printf( "CFA array alloc_align, fill\n" );
     
    229290        free( stp );
    230291
    231         stp = alloc_align_set( Alignment, dim, (Struct){ 42, 42.5 } ); // CFA array memalign, fill
     292        stp = alloc( dim, Alignment`align, ((Struct){ 42, 42.5 })`fill ); // CFA array memalign, fill
    232293        assert( (uintptr_t)stp % Alignment == 0 );
    233294        printf( "CFA array alloc_align, fill\n" );
     
    236297        // do not free
    237298
    238         stp1 = alloc_align_set( Alignment, dim, stp );          // CFA array memalign, fill
     299        stp1 = alloc( dim, Alignment`align, [stp, dim]`fill );  // CFA array memalign, fill
    239300        assert( (uintptr_t)stp % Alignment == 0 );
    240301        printf( "CFA array alloc_align, fill array\n" );
     
    243304        free( stp1 );
    244305
    245         stp = alloc_align( stp, 4096, dim );                            // CFA aligned realloc array
     306        stp = alloc( dim, stp`realloc, 4096`align );                            // CFA aligned realloc array
    246307        assert( (uintptr_t)stp % 4096 == 0 );
    247308        for ( i; dim ) { stp[i] = (Struct){ 42, 42.5 }; }
     
    274335        printf( "\n" );
    275336
    276 
    277337        // new, non-array types
    278338        printf( "\n" );
    279339
     340        const_count = dest_count = 0;
    280341        stp = new( 42, 42.5 );
     342        assert( const_count == 1 && dest_count == 0 );                                          // assertion for testing
    281343        stp1 = new( 42, 42.5 );
     344        assert( const_count == 2 && dest_count == 0 );                                          // assertion for testing
     345
    282346        printf( "CFA new initialize\n%d %g %d %g\n", stp->x, stp->y, stp1->x, stp1->y );
    283347        delete( stp, stp1 );
     348        assert( const_count == 2 && dest_count == 2 );                                          // assertion for testing
    284349
    285350        // new, array types
    286351        stp = anew( dim, 42, 42.5 );
     352        assert( const_count == 2 + dim && dest_count == 2 );                            // assertion for testing
    287353        printf( "CFA array new initialize\n" );
    288354        for ( i; dim ) { printf( "%d %g, ", stp[i].x, stp[i].y ); }
    289355        printf( "\n" );
     356
    290357        stp1 = anew( dim, 42, 42.5 );
     358        assert( const_count == 2 + 2 * dim && dest_count == 2 );                        // assertion for testing
    291359        for ( i; dim ) { printf( "%d %g, ", stp1[i].x, stp1[i].y ); }
    292360        printf( "\n" );
    293         adelete( dim, stp, dim, stp1 );
     361        adelete( stp, stp1 );
     362        assert( const_count == 2 + 2 * dim && dest_count == 2 + 2 * dim);       // assertion for testing
    294363
    295364        // extras
     
    300369        free( fp - 1 );
    301370
    302         p = foo( bar( baz( malloc(), 0 ), 0 ), 0 );
    303         *p = 0xdeadbeef;
    304         printf( "CFA deep malloc %#x\n", *p );
    305         free( p );
     371        ip = foo( bar( baz( malloc(), 0 ), 0 ), 0 );
     372        *ip = 0xdeadbeef;
     373        printf( "CFA deep malloc %#x\n", *ip );
     374
     375        dp = alloc(5.0`fill); // just for testing multiple free
     376        assert(*dp == 5.0);
     377        free( ip, dp, 0p );
    306378
    307379#ifdef ERR1
    308380        stp = malloc();
    309381        printf( "\nSHOULD FAIL\n" );
    310         p = realloc( stp, dim * sizeof( *stp ) );
    311         p = alloc( stp, dim * sizeof( *stp ) );
    312         p = memset( stp, 10 );
    313         p = memcpy( &st1, &st );
    314 #endif
     382        ip = realloc( stp, dim * sizeof( *stp ) );
     383        ip = memset( stp, 10 );
     384        ip = memcpy( &st1, &st );
     385#endif // ERR1
    315386} // main
    316387
  • tests/array.cfa

    r3c64c668 r58fe85a  
    1 //                               -*- Mode: C -*- 
    2 // 
     1//                               -*- Mode: C -*-
     2//
    33// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
    44//
    55// The contents of this file are covered under the licence agreement in the
    66// file "LICENCE" distributed with Cforall.
    7 // 
     7//
    88// array.cfa -- test array declarations
    9 // 
     9//
    1010// Author           : Peter A. Buhr
    1111// Created On       : Tue Feb 19 21:18:06 2019
    1212// Last Modified By : Peter A. Buhr
    13 // Last Modified On : Tue Feb 19 21:18:46 2019
    14 // Update Count     : 1
    15 // 
     13// Last Modified On : Sun Sep 27 09:05:40 2020
     14// Update Count     : 4
     15//
    1616
    17 int a1[];
     17int a1[0];
    1818//int a2[*];
    1919//double a4[3.0];
    2020
    21 int m1[][3];
     21int m1[0][3];
    2222//int m2[*][*];
    2323int m4[3][3];
     
    4949}
    5050
    51 int main() {}
     51int main() {
     52        #if !defined(NO_COMPILED_PRAGMA)
     53                #pragma message( "Compiled" )   // force non-empty .expect file
     54        #endif
     55}
    5256
    5357// Local Variables: //
  • tests/avltree/avl1.cfa

    r3c64c668 r58fe85a  
    2424tree(K, V) * create(K key, V value) {
    2525  // infinite loop trying to resolve ... t = malloc();
    26   tree(K, V) * t = malloc(sizeof(tree(K,V)));
     26  tree(K, V) * t = ( tree(K, V) * ) malloc(sizeof(tree(K,V)));
    2727  (*t){ key, value };
    2828  return t;
  • tests/builtins/.expect/sync.txt

    r3c64c668 r58fe85a  
     1builtins/sync.cfa: In function '_X4mainFi___1':
     2builtins/sync.cfa:358:9: note: #pragma message: Compiled
  • tests/builtins/sync.cfa

    r3c64c668 r58fe85a  
    6666        #if defined(__SIZEOF_INT128__)
    6767        { __int128 ret; ret = __sync_fetch_and_nand(vplll, vlll); }
    68         { __int128 ret; ret = __sync_fetch_and_nand_16(vplll, vlll); }
    6968        #endif
    7069
     
    355354
    356355int main() {
    357         return 0;
     356        #pragma message( "Compiled" )                   // force non-empty .expect file
    358357}
  • tests/cast.cfa

    r3c64c668 r58fe85a  
    1313
    1414//Dummy main
    15 int main(int argc, char const *argv[])
    16 {
    17         return 0;
     15int main( int argc, char const * argv[] ) {
     16        #pragma message( "Compiled" )                   // force non-empty .expect file
    1817}
  • tests/castError.cfa

    r3c64c668 r58fe85a  
    1414//
    1515
     16forall(otype T) struct S { T p; };
    1617int f;
     18S(int) sint;
    1719
    1820void f() {
     
    2527        short int v;
    2628        3, v;           // implicit void cast
     29
     30        (S(char)) sint;
    2731}
    2832
  • tests/complex.cfa

    r3c64c668 r58fe85a  
    1414//
    1515
    16 #include <stdio.h>
    1716#include <complex.h>
    1817#ifdef __CFA__
  • tests/concurrent/.expect/monitor.txt

    r3c64c668 r58fe85a  
    1 4000000
     13000000
  • tests/concurrent/coroutineYield.cfa

    r3c64c668 r58fe85a  
    3333                        sout | "Coroutine 2";
    3434                #endif
    35                 suspend();
     35                suspend;
    3636        }
    3737}
  • tests/concurrent/examples/.expect/datingService.txt

    r3c64c668 r58fe85a  
     1done
  • tests/concurrent/examples/boundedBufferEXT.cfa

    r3c64c668 r58fe85a  
    11//
    22// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
    3 // 
     3//
    44// The contents of this file are covered under the licence agreement in the
    55// file "LICENCE" distributed with Cforall.
     
    8787}
    8888
     89enum { Prods = 4, Cons = 5 };
     90Producer * prods[Prods];
     91Consumer * cons[Cons];
     92
    8993int main() {
    9094        Buffer(int) buffer;
    91         enum { Prods = 4, Cons = 5 };
    92         Producer * prods[Prods];
    93         Consumer * cons[Cons];
    9495        int sums[Cons];
    9596        int i;
  • tests/concurrent/examples/datingService.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Mon Oct 30 12:56:20 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Jun 21 11:32:34 2019
    13 // Update Count     : 38
     12// Last Modified On : Sun Sep 27 15:42:25 2020
     13// Update Count     : 40
    1414//
    1515
     
    3535                signal_block( Boys[ccode] );                                    // restart boy to set phone number
    3636        } // if
    37         //sout | "Girl:" | PhoneNo | "is dating Boy at" | BoyPhoneNo | "with ccode" | ccode;
     37        // sout | "Girl:" | PhoneNo | "is dating Boy at" | BoyPhoneNo | "with ccode" | ccode;
    3838        return BoyPhoneNo;
    3939} // DatingService girl
     
    4747                signal_block( Girls[ccode] );                                   // restart girl to set phone number
    4848        } // if
    49         //sout | " Boy:" | PhoneNo | "is dating Girl" | GirlPhoneNo | "with ccode" | ccode;
     49        // sout | " Boy:" | PhoneNo | "is dating Girl" | GirlPhoneNo | "with ccode" | ccode;
    5050        return GirlPhoneNo;
    5151} // DatingService boy
     
    108108                if ( girlck[ boyck[i] ] != boyck[ girlck[i] ] ) abort();
    109109        } // for
     110
     111        printf( "done\n" );                                                                     // non-empty .expect file
    110112} // main
    111113
  • tests/concurrent/monitor.cfa

    r3c64c668 r58fe85a  
    2929
    3030void main( MyThread & this ) {
    31         for(int i = 0; i < 1_000_000; i++) {
     31        for(int i = 0; i < 750_000; i++) {
    3232                increment( global );
    3333        }
  • tests/concurrent/park/.expect/force_preempt.txt

    r3c64c668 r58fe85a  
    1 0 Calling unpark 0
    2 1 Calling unpark 0
    3 2 Calling unpark 0
    4 3 Calling unpark 0
    5 4 Calling unpark 0
    6 0 Parking 0
    7 0 Unparked 0
    8 0 Calling unpark 1
    9 1 Parking 0
    10 1 Unparked 0
    11 1 Calling unpark 1
    12 2 Parking 0
    13 2 Unparked 0
    14 2 Calling unpark 1
    15 3 Parking 0
    16 3 Unparked 0
    17 3 Calling unpark 1
    18 4 Parking 0
    19 4 Unparked 0
    20 4 Calling unpark 1
    21 0 Parking 1
    22 0 Unparked 1
    23 0 Calling unpark 2
    24 1 Parking 1
    25 1 Unparked 1
    26 1 Calling unpark 2
    27 2 Parking 1
    28 2 Unparked 1
    29 2 Calling unpark 2
    30 3 Parking 1
    31 3 Unparked 1
    32 3 Calling unpark 2
    33 4 Parking 1
    34 4 Unparked 1
    35 4 Calling unpark 2
    36 0 Parking 2
    37 0 Unparked 2
    38 0 Calling unpark 3
    39 1 Parking 2
    40 1 Unparked 2
    41 1 Calling unpark 3
    42 2 Parking 2
    43 2 Unparked 2
    44 2 Calling unpark 3
    45 3 Parking 2
    46 3 Unparked 2
    47 3 Calling unpark 3
    48 4 Parking 2
    49 4 Unparked 2
    50 4 Calling unpark 3
    51 0 Parking 3
    52 0 Unparked 3
    53 0 Calling unpark 4
    54 1 Parking 3
    55 1 Unparked 3
    56 1 Calling unpark 4
    57 2 Parking 3
    58 2 Unparked 3
    59 2 Calling unpark 4
    60 3 Parking 3
    61 3 Unparked 3
    62 3 Calling unpark 4
    63 4 Parking 3
    64 4 Unparked 3
    65 4 Calling unpark 4
    66 0 Parking 4
    67 0 Unparked 4
    68 1 Parking 4
    69 1 Unparked 4
    70 2 Parking 4
    71 2 Unparked 4
    72 3 Parking 4
    73 3 Unparked 4
    74 4 Parking 4
    75 4 Unparked 4
     1done
  • tests/concurrent/park/contention.cfa

    r3c64c668 r58fe85a  
    2424                } else {
    2525                        Thread * thrd = __atomic_exchange_n(&blocked[idx], &this, __ATOMIC_SEQ_CST);
    26                         unpark( *thrd);
     26                        unpark( *thrd );
    2727                        park();
    2828                }
  • tests/concurrent/park/force_preempt.cfa

    r3c64c668 r58fe85a  
    1616}
    1717
    18 thread Waiter;
    1918thread Waiter {};
    20 
    2119
    2220volatile int count = 0;
     
    2523        // Get a unique id
    2624        int id = __atomic_fetch_add(&count, 1, __ATOMIC_SEQ_CST);
     25        int id_hash = id | (id << 8) | (id << 16) | (id << 24);
     26        int mask = 0xCAFEBABA;
    2727
    2828        for(int i = 0; i < 5; i++) {
     29                assert(mask == 0xCAFEBABA);
     30
    2931                // Unpark this thread, don't force a yield
    30                 sout | id | "Calling unpark" | i;
    31                 unpark(this);
     32                unpark( this );
     33                assert(mask == 0xCAFEBABA);
     34
     35                // Hash the mask to make sure no one else messes with them
     36                mask ^= id_hash;
     37                assert(mask == (id_hash ^ 0xCAFEBABA));
    3238
    3339                // Force a preemption before the call to park
     
    3642
    3743                // Park this thread,
    38                 sout | id | "Parking" | i;
     44                assert(mask == (id_hash ^ 0xCAFEBABA));
    3945                park();
    40                 sout | id | "Unparked" | i;
     46                assert(mask == (id_hash ^ 0xCAFEBABA));
     47
     48                // Reset the hash and recheck it
     49                mask ^= id_hash;
     50                assert(mask == 0xCAFEBABA);
    4151        }
    4252}
     
    4757                Waiter waiters[5];
    4858        }
     59        printf( "done\n" );                             // non-empty .expect file
    4960}
  • tests/concurrent/signal/block.cfa

    r3c64c668 r58fe85a  
    8282        if( !is_empty( cond ) ) {
    8383
    84                 $thread * next = front( cond );
     84                $thread * next = ( $thread * ) front( cond );
    8585
    8686                if( ! signal_block( cond ) ) {
  • tests/concurrent/signal/disjoint.cfa

    r3c64c668 r58fe85a  
    2121#endif
    2222
     23// This tests checks what happens when someone barges in the midle of the release
     24// of a bulk of monitors.
     25
    2326enum state_t { WAIT, SIGNAL, BARGE };
    2427
    2528monitor global_t {};
    26 global_t mut;
    2729
    2830monitor global_data_t;
     
    3335        int counter;
    3436        state_t state;
    35 } data;
     37};
     38
     39// Use a global struct because the order needs to match with Signaller thread
     40struct {
     41        global_t mut;
     42        global_data_t data;
     43} globals;
    3644
    3745condition cond;
     
    4048
    4149void ?{}( global_data_t & this ) {
    42         this.counter == 0;
     50        this.counter = 0;
    4351        this.state = BARGE;
    4452}
     
    5361
    5462thread Barger {};
     63void ?{}( Barger & this ) {
     64        ((thread&)this){ "Barger Thread" };
     65}
    5566
    5667void main( Barger & this ) {
    5768        while( !all_done ) {
    58                 barge( data );
     69                barge( globals.data );
    5970                yield();
    6071        }
     
    7889
    7990thread Waiter {};
     91void ?{}( Waiter & this ) {
     92        ((thread&)this){ "Waiter Thread" };
     93}
    8094
    8195void main( Waiter & this ) {
    82         while( wait( mut, data ) ) { KICK_WATCHDOG; yield(); }
     96        while( wait( globals.mut, globals.data ) ) { KICK_WATCHDOG; yield(); }
    8397}
    8498
     
    92106
    93107void logic( global_t & mutex a ) {
    94         signal( cond, a, data );
     108        signal( cond, a, globals.data );
    95109
    96110        yield( random( 10 ) );
    97111
    98112        //This is technically a mutual exclusion violation but the mutex monitor protects us
    99         bool running = TEST(data.counter < N) && data.counter > 0;
    100         if( data.state != SIGNAL && running ) {
    101                 sout | "ERROR Eager signal" | data.state;
     113        bool running = TEST(globals.data.counter < N) && globals.data.counter > 0;
     114        if( globals.data.state != SIGNAL && running ) {
     115                sout | "ERROR Eager signal" | globals.data.state;
    102116        }
    103117}
    104118
    105119thread Signaller {};
     120void ?{}( Signaller & this ) {
     121        ((thread&)this){ "Signaller Thread" };
     122}
    106123
    107124void main( Signaller & this ) {
    108125        while( !all_done ) {
    109                 logic( mut );
     126                logic( globals.mut );
    110127                yield();
    111128        }
  • tests/concurrent/waitfor/when.cfa

    r3c64c668 r58fe85a  
    5757
    5858void arbiter( global_t & mutex this ) {
     59        // There is a race at start where callers can get in before the arbiter.
     60        // It doesn't really matter here so just restart the loop correctly and move on
     61        this.last_call = 6;
     62
    5963        for( int i = 0; i < N; i++ ) {
    6064                   when( this.last_call == 6 ) waitfor( call1 : this ) { if( this.last_call != 1) { serr | "Expected last_call to be 1 got" | this.last_call; } }
  • tests/config.py.in

    r3c64c668 r58fe85a  
    99HOSTARCH = "@host_cpu@"
    1010DISTRIBUTE = @HAS_DISTCC@
     11NEWAST = @DEFAULT_NEW_AST@
  • tests/copyfile.cfa

    r3c64c668 r58fe85a  
    88//
    99// Author           : Peter A. Buhr
    10 // Created On       : Tue Jul 16 16:47:22 2019
     10// Created On       : Fri Jun 19 13:44:05 2020
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Jul 17 18:04:44 2019
    13 // Update Count     : 26
     12// Last Modified On : Sat Aug 15 15:00:48 2020
     13// Update Count     : 6
    1414//
    1515
    1616#include <fstream.hfa>
    17 #include <stdlib.hfa>                                                                   // new/delete
     17#include <exception.hfa>
    1818
    1919int main( int argc, char * argv[] ) {
    20         ifstream * in  = &stdin;                                                        // default files
    21         ofstream * out = &stdout;
     20        ifstream in  = stdin;                                                           // copy default files
     21        ofstream out = stdout;
     22
    2223        try {
    23                 choose ( argc ) {
     24                choose ( argc ) {                                                               // terminate if command-line errors
    2425                  case 2, 3:
    25                           in = new( (const char *)argv[1] );            // open input file first as output creates file
    26                           if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent
    27                   case 1: ;                                     // use default files
    28                   default:
    29                           exit | "Usage [ input-file (default stdin) [ output-file (default stdout) ] ]";
     26                        open( in, argv[1] );                                            // open input file first as output creates file
     27                        if ( argc == 3 ) open( out, argv[2] );          // do not create output unless input opens
     28                  case 1: ;                                                                             // use default files
     29                  default:                                                                              // wrong number of options
     30                        exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]";
    3031                } // choose
     32        } catch( Open_Failure * ex ; ex->istream == &in ) {
     33                exit | "Unable to open input file" | argv[1];
     34        } catch( Open_Failure * ex ; ex->ostream == &out ) {
     35                close( in );                                                                    // optional
     36                exit | "Unable to open output file" | argv[2];
     37        } // try
    3138
    32                 char ch;
    33                 *out | nlOff;                                                                   // turn off auto newline
    34                 *in  | nlOn;                                                                    // turn on reading newline
     39        out | nlOff;                                                                            // turn off auto newline
     40        in  | nlOn;                                                                                     // turn on reading newline
    3541
    36                 for () {                                                                                // read all characters
    37                         *in | ch;
    38                   if ( eof( *in ) ) break;                                              // eof ?
    39                         *out | ch;
    40                 } // for
    41         } finally {
    42                 if ( in  != &stdin  ) delete( in );                             // close file, do not delete stdin!
    43                 if ( out != &stdout ) delete( out );                    // close file, do not delete stdout!
    44         } // try
     42        char ch;
     43        for () {                                                                                        // read all characters
     44                in | ch;
     45          if ( eof( in ) ) break;                                                       // eof ?
     46                out | ch;
     47        } // for
    4548} // main
    4649
  • tests/coroutine/.expect/fmtLines.txt

    r3c64c668 r58fe85a  
    4848{                                                         // f  or n  ewli 
    4949ne c  hara  cter  s                                     su 
    50 spen  d();                                      if   ( fm 
    51 t.ch   !=   '\n'   ) b  reak 
    52 ;               /  / ig  nore   new  line 
    53                                   } //   for                              sout 
    54  | f  mt.c  h;                                                  //  
    55 prin  t ch  arac  ter                   }  
    56 // f  or                        sou  t |   "  " 
    57 ;                                                               //   prin  t bl 
    58 ock   sepa  rato  r             }   //  
    59 for             sou  t |   nl;                                   
    60                                   // p  rint   gro  up s 
    61 epar  ator      } /  / fo  r} / 
    62 / ma  invo  id p  rt(   Form 
    63 at &   fmt  , ch  ar c  h )   
    64 {      fmt  .ch   = ch  ;    
    65  res  ume(   fmt   );}   //  
    66 prti  nt m  ain(  ) {     Form 
    67 at f  mt;         char   ch;    for 
    68  ( ;  ; )   {           s  in |   ch; 
    69                                                                                 //   rea  d on 
    70 e ch  arac  ter     if   ( e 
    71 of(   sin   ) )   brea  k;               
    72                                         //   eof   ?            p  rt(  
    73 fmt,   ch   );  }   //   for} 
    74  //   main  // L  ocal   Var 
    75 iabl  es:   ////   tab  -wid 
    76 th:   4 //  // c  ompi  le-c 
    77 omma  nd:   "cfa   fmt  Line 
    78 s.cf  a" /  ///   End:   //
     50spen  d;                                        i  f (   fmt. 
     51ch !  = '\  n' )   bre  ak;      
     52        //   igno  re n  ewli  ne                
     53                }   // f  or                            so  ut | 
     54 fmt  .ch;                                                      /  / pr 
     55int   char  acte  r                       } // 
     56 for                    s  out   | "    ";       
     57                                                        /  / pr  int   bloc 
     58k se  para  tor         } /  / fo 
     59r               s  out   | nl  ;                                                         
     60                //   pri  nt g  roup   sep 
     61arat  or        }   //   for}   //  
     62main  void   prt  ( Fo  rmat 
     63 & f  mt,   char   ch   ) {   
     64   f  mt.c  h =   ch;      r 
     65esum  e( f  mt )  ;} /  / pr 
     66tint   mai  n()   {     Fo  rmat 
     67 fmt  ; ch  ar c  h;    f  or ( 
     68 ;;   ) {               sin   | c  h;            
     69                                                                  // r  ead   one  
     70char  acte  r       if (   eof 
     71( si  n )   ) br  eak;                                   
     72                        /  / eo  f ?            prt  ( fm 
     73t, c  h );      } /  / fo  r} / 
     74/ ma  in//   Loc  al V  aria 
     75bles  : //  // t  ab-w  idth 
     76: 4   ////   com  pile  -com 
     77mand  : "c  fa f  mtLi  nes. 
     78cfa"   ///  / En  d: /  /
  • tests/coroutine/.in/fmtLines.txt

    r3c64c668 r58fe85a  
    3535                        for ( fmt.b = 0; fmt.b < 4; fmt.b += 1 ) {      // blocks of 4 characters
    3636                                for ( ;; ) {                                                    // for newline characters
    37                                         suspend();
     37                                        suspend;
    3838                                        if ( fmt.ch != '\n' ) break;            // ignore newline
    3939                                } // for
  • tests/coroutine/cntparens.cfa

    r3c64c668 r58fe85a  
    1 // 
     1//
    22// Cforall Version 1.0.0 Copyright (C) 2017 University of Waterloo
    33//
    44// The contents of this file are covered under the licence agreement in the
    55// file "LICENCE" distributed with Cforall.
    6 // 
     6//
    77// cntparens.cfa -- match left/right parenthesis
    8 // 
     8//
    99// Author           : Peter A. Buhr
    1010// Created On       : Sat Apr 20 11:04:45 2019
     
    1212// Last Modified On : Sat Apr 20 11:06:21 2019
    1313// Update Count     : 1
    14 // 
     14//
    1515
    1616#include <fstream.hfa>
     
    2626void main( CntParens & cpns ) with( cpns ) {
    2727        for ( ; ch == '('; cnt += 1 ) {                                         // left parenthesis
    28                 suspend();
     28                suspend;
    2929        }
    3030        for ( ; ch == ')' && cnt > 1; cnt -= 1 ) {                      // right parenthesis
    31                 suspend();
     31                suspend;
    3232        }
    3333        status = ch == ')' ? Match : Error;
    3434} // main
    35        
     35
    3636void ?{}( CntParens & cpns ) with( cpns ) { status = Cont; cnt = 0; }
    3737
  • tests/coroutine/devicedriver.cfa

    r3c64c668 r58fe85a  
    1 // 
     1//
    22// Cforall Version 1.0.0 Copyright (C) 2017 University of Waterloo
    33//
    44// The contents of this file are covered under the licence agreement in the
    55// file "LICENCE" distributed with Cforall.
    6 // 
    7 // devicedriver.cfa -- 
    8 // 
     6//
     7// devicedriver.cfa --
     8//
    99// Author           : Peter A. Buhr
    1010// Created On       : Sat Mar 16 15:30:34 2019
     
    1212// Last Modified On : Sat Apr 20 09:07:19 2019
    1313// Update Count     : 90
    14 // 
     14//
    1515
    1616#include <fstream.hfa>
     
    2929
    3030void checkCRC( Driver & d, unsigned int sum ) with( d ) {
    31         suspend();
     31        suspend;
    3232        unsigned short int crc = byte << 8;                                     // sign extension over written
    33         suspend();
     33        suspend;
    3434        // prevent sign extension for signed char
    3535        status = (crc | (unsigned char)byte) == sum ? MSG : ECRC;
     
    4141                status = CONT;
    4242                unsigned int lnth = 0, sum = 0;
    43                 while ( byte != STX ) suspend();
     43                while ( byte != STX ) suspend;
    4444          emsg: for () {
    45                         suspend();
     45                        suspend;
    4646                        choose ( byte ) {                                                       // process byte
    4747                          case STX:
    48                                 status = ESTX; suspend(); continue msg;
     48                                status = ESTX; suspend; continue msg;
    4949                          case ETX:
    5050                                break emsg;
    5151                          case ESC:
    52                                 suspend();
     52                                suspend;
    5353                        } // choose
    5454                        if ( lnth >= MaxMsg ) {                                         // buffer full ?
    55                                 status = ELNTH; suspend(); continue msg;
     55                                status = ELNTH; suspend; continue msg;
    5656                        } // if
    5757                        msg[lnth++] = byte;
     
    6060                msg[lnth] = '\0';                                                               // terminate string
    6161                checkCRC( d, sum );                                                             // refactor CRC check
    62                 suspend();
     62                suspend;
    6363        } // for
    6464} // main
  • tests/coroutine/fibonacci.cfa

    r3c64c668 r58fe85a  
    2222        int fn1, fn2;                                                                           // retained between resumes
    2323        fn = 0;  fn1 = fn;                                                                      // 1st case
    24         suspend();                                                                                      // restart last resume
     24        suspend;                                                                                        // restart last resume
    2525        fn = 1;  fn2 = fn1;  fn1 = fn;                                          // 2nd case
    26         suspend();                                                                                      // restart last resume
     26        suspend;                                                                                        // restart last resume
    2727        for () {
    2828                fn = fn1 + fn2;  fn2 = fn1;  fn1 = fn;                  // general case
    29                 suspend();                                                                              // restart last resume
     29                suspend;                                                                                // restart last resume
    3030        } // for
    3131}
  • tests/coroutine/fibonacci_1.cfa

    r3c64c668 r58fe85a  
    1212// Last Modified On : Thu Mar 21 08:10:45 2019
    1313// Update Count     : 25
    14 // 
     14//
    1515
    1616#include <fstream.hfa>
     
    2323        [fn1, fn] = [0, 1];                                                                     // precompute first two states
    2424        for () {
    25                 suspend();                                                                              // restart last resume
     25                suspend;                                                                                // restart last resume
    2626                [fn1, fn] = [fn, fn1 + fn];                                             // general case
    2727        } // for
  • tests/coroutine/fmtLines.cfa

    r3c64c668 r58fe85a  
    2727                        for ( b = 0; b < 4; b += 1 ) {                          // blocks of 4 characters
    2828                                for () {                                                                // for newline characters
    29                                         suspend();
     29                                        suspend;
    3030                                  if ( ch != '\n' ) break;                              // ignore newline
    3131                                } // for
  • tests/coroutine/raii.cfa

    r3c64c668 r58fe85a  
    3939        Raii raii = { "Coroutine" };
    4040        sout | "Before Suspend";
    41         suspend();
     41        suspend;
    4242        sout | "After Suspend";
    4343}
  • tests/coroutine/runningTotal.cfa

    r3c64c668 r58fe85a  
    2525void update( RunTotal & rntl, int input ) with( rntl ) { // helper
    2626        total += input;                                                                         // remember between activations
    27         suspend();                                                                                      // inactivate on stack
     27        suspend;                                                                                        // inactivate on stack
    2828}
    2929
  • tests/coroutine/suspend_then.cfa

    r3c64c668 r58fe85a  
    1515
    1616#include <fstream.hfa>
    17 #include <coroutine.hfa>
    1817
    19 void then() {
    20         sout | "Then!";
    21 }
    22 
    23 coroutine Fibonacci { int fn; };                                                // used for communication
     18generator Fibonacci {
     19        int fn;                                                                         // used for communication
     20        int fn1, fn2;                                                           // retained between resumes
     21};
    2422
    2523void main( Fibonacci & fib ) with( fib ) {                              // called on first resume
    26         int fn1, fn2;                                                           // retained between resumes
    2724        fn = 0;  fn1 = fn;                                                      // 1st case
    28         suspend_then(then);                                                     // restart last resume
     25        suspend { sout | "Then!"; }                                             // restart last resume
    2926        fn = 1;  fn2 = fn1;  fn1 = fn;                                  // 2nd case
    30         suspend_then(then);                                                     // restart last resume
     27        suspend { sout | "Then!"; }                                             // restart last resume
    3128        for () {
    3229                fn = fn1 + fn2;  fn2 = fn1;  fn1 = fn;                  // general case
    33                 suspend_then(then);                                             // restart last resume
     30                suspend { sout | "Then!"; }                                     // restart last resume
    3431        } // for
    3532}
  • tests/enum.cfa

    r3c64c668 r58fe85a  
    2626//Dummy main
    2727int main(int argc, char const *argv[]) {
     28        printf( "done\n" );                             // non-empty .expect file
    2829}
  • tests/expression.cfa

    r3c64c668 r58fe85a  
    88
    99int main() {
    10     int a[3] = { 0, 0, 0 };
    11     S s = { 3 }, * ps = &s;
    12     [int] t = { 3 };
    13     * [int] pt = &t;
    14     int i = 1, j = 2;
     10        int a[3] = { 0, 0, 0 };
     11        S s = { 3 }, * ps = &s;
     12        [int] t = { 3 };
     13        * [int] pt = &t;
     14        int i = 1, j = 2;
    1515
    16     // operators
     16        // operators
    1717
    18     !i;
    19     ~i;
    20     +i;
    21     -i;
    22     *ps;
    23     ++ps;
    24     --ps;
    25     ps++;
    26     ps--;
     18        !i;
     19        ~i;
     20        +i;
     21        -i;
     22        *ps;
     23        ++ps;
     24        --ps;
     25        ps++;
     26        ps--;
    2727
    28     i + j;
    29     i - j;
    30     i * j;
     28        i + j;
     29        i - j;
     30        i * j;
    3131
    32     i / j;
    33     i % j;
    34     i ^ j;
    35     i & j;
    36     i | j;
    37     i < j;
    38     i > j;
    39     i = j;
     32        i / j;
     33        i % j;
     34        i ^ j;
     35        i & j;
     36        i | j;
     37        i < j;
     38        i > j;
     39        i = j;
    4040
    41     i == j;
    42     i != j;
    43     i << j;
    44     i >> j;
    45     i <= j;
    46     i >= j;
    47     i && j;
    48     i || j;
    49     ps->i;
     41        i == j;
     42        i != j;
     43        i << j;
     44        i >> j;
     45        i <= j;
     46        i >= j;
     47        i && j;
     48        i || j;
     49        ps->i;
    5050
    51     i *= j;
    52     i /= j;
    53     i %= j;
    54     i += j;
    55     i -= j;
    56     i &= j;
    57     i |= j;
    58     i ^= j;
    59     i <<= j;
    60     i >>= j;
     51        i *= j;
     52        i /= j;
     53        i %= j;
     54        i += j;
     55        i -= j;
     56        i &= j;
     57        i |= j;
     58        i ^= j;
     59        i <<= j;
     60        i >>= j;
    6161
    62     i ? i : j;
     62        i ? i : j;
    6363
    64     // postfix function call
     64        // postfix function call
    6565
    66     (3 + 4)`mary;
    67     ({3 + 4;})`mary;
    68     [3, 4]`mary;
    69     3`mary;
    70     a[0]`mary;
    71     a[0]`mary`mary;
    72     s{0}`mary;
    73     a[3]`jane++;
    74     jack(3)`mary;
    75     s.i`mary;
    76     t.0`mary;
    77     s.[i]`mary;
    78     ps->i`mary;
    79     pt->0`mary;
    80     ps->[i]`mary;
    81     i++`mary;
    82     i--`mary;
    83     (S){2}`mary;
    84     (S)@{2}`mary;
     66        (3 + 4)`mary;
     67        ({3 + 4;})`mary;
     68        [3, 4]`mary;
     69        3`mary;
     70        a[0]`mary;
     71        a[0]`mary`mary;
     72        s{0}`mary;
     73        a[3]`jane++;
     74        jack(3)`mary;
     75        s.i`mary;
     76        t.0`mary;
     77        s.[i]`mary;
     78        ps->i`mary;
     79        pt->0`mary;
     80        ps->[i]`mary;
     81        i++`mary;
     82        i--`mary;
     83        (S){2}`mary;
     84        (S)@{2}`mary;
     85
     86        #if !defined(NO_COMPILED_PRAGMA)
     87                #pragma message( "Compiled" )   // force non-empty .expect file
     88        #endif
    8589} // main
  • tests/forall.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May  9 08:48:15 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Mar 19 08:29:38 2019
    13 // Update Count     : 32
     12// Last Modified On : Sun Sep 27 08:43:20 2020
     13// Update Count     : 35
    1414//
    1515
     
    158158}
    159159forall( otype T ) inline static {
    160         int RT9( T ) { T t; }
     160        int RT9( T ) { T t; return 3; }
    161161}
    162162
     
    213213// w3 g3;
    214214
    215 int main( void ) {}
     215int main( void ) {
     216        #pragma message( "Compiled" )                   // force non-empty .expect file
     217}
    216218
    217219// Local Variables: //
  • tests/heap.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Nov  6 17:54:56 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Nov 24 12:34:51 2019
    13 // Update Count     : 28
     12// Last Modified On : Tue Dec 15 12:11:51 2020
     13// Update Count     : 79
    1414//
    1515
     
    2727// }
    2828
    29 #define __U_DEFAULT_MMAP_START__ (512 * 1024 + 1)
    30 size_t default_mmap_start() __attribute__(( weak )) {
    31         return __U_DEFAULT_MMAP_START__;
     29size_t default_heap_expansion() {
     30        return 10 * 1024 * 1024;
     31} // default_heap_expansion
     32
     33size_t default_mmap_start() {
     34        return 512 * 1024 + 1;
    3235} // default_mmap_start
    3336
     
    7578                size_t s = (i + 1) * 20;
    7679                char * area = (char *)malloc( s );
    77                 if ( area == 0p ) abort( "malloc/free out of memory" );
    7880                area[0] = '\345'; area[s - 1] = '\345';                 // fill first/last
    7981                area[malloc_usable_size( area ) - 1] = '\345';  // fill ultimate byte
     
    8486                size_t s = i + 1;                                                               // +1 to make initialization simpler
    8587                locns[i] = (char *)malloc( s );
    86                 if ( locns[i] == 0p ) abort( "malloc/free out of memory" );
    8788                locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last
    8889                locns[i][malloc_usable_size( locns[i] ) - 1] = '\345'; // fill ultimate byte
     
    100101                size_t s = i + default_mmap_start();                    // cross over point
    101102                char * area = (char *)malloc( s );
    102                 if ( area == 0p ) abort( "malloc/free out of memory" );
    103103                area[0] = '\345'; area[s - 1] = '\345';                 // fill first/last
    104104                area[malloc_usable_size( area ) - 1] = '\345';  // fill ultimate byte
     
    109109                size_t s = i + default_mmap_start();                    // cross over point
    110110                locns[i] = (char *)malloc( s );
    111                 if ( locns[i] == 0p ) abort( "malloc/free out of memory" );
    112111                locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last
    113112                locns[i][malloc_usable_size( locns[i] ) - 1] = '\345'; // fill ultimate byte
     
    125124                size_t s = (i + 1) * 20;
    126125                char * area = (char *)calloc( 5, s );
    127                 if ( area == 0p ) abort( "calloc/free out of memory" );
    128126                if ( area[0] != '\0' || area[s - 1] != '\0' ||
    129                          area[malloc_usable_size( area ) - 1] != '\0' ||
     127                         area[malloc_size( area ) - 1] != '\0' ||
    130128                         ! malloc_zero_fill( area ) ) abort( "calloc/free corrupt storage1" );
    131129                area[0] = '\345'; area[s - 1] = '\345';                 // fill first/last
     
    137135                size_t s = i + 1;
    138136                locns[i] = (char *)calloc( 5, s );
    139                 if ( locns[i] == 0p ) abort( "calloc/free out of memory" );
    140137                if ( locns[i][0] != '\0' || locns[i][s - 1] != '\0' ||
    141                          locns[i][malloc_usable_size( locns[i] ) - 1] != '\0' ||
     138                         locns[i][malloc_size( locns[i] ) - 1] != '\0' ||
    142139                         ! malloc_zero_fill( locns[i] ) ) abort( "calloc/free corrupt storage2" );
    143140                locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last
     
    156153                size_t s = i + default_mmap_start();                    // cross over point
    157154                char * area = (char *)calloc( 1, s );
    158                 if ( area == 0p ) abort( "calloc/free out of memory" );
    159155                if ( area[0] != '\0' || area[s - 1] != '\0' ) abort( "calloc/free corrupt storage4.1" );
    160                 if ( area[malloc_usable_size( area ) - 1] != '\0' ) abort( "calloc/free corrupt storage4.2" );
     156                if ( area[malloc_size( area ) - 1] != '\0' ) abort( "calloc/free corrupt storage4.2" );
    161157                if ( ! malloc_zero_fill( area ) ) abort( "calloc/free corrupt storage4.3" );
    162158                area[0] = '\345'; area[s - 1] = '\345';                 // fill first/last
     
    168164                size_t s = i + default_mmap_start();                    // cross over point
    169165                locns[i] = (char *)calloc( 1, s );
    170                 if ( locns[i] == 0p ) abort( "calloc/free out of memory" );
    171166                if ( locns[i][0] != '\0' || locns[i][s - 1] != '\0' ||
    172                          locns[i][malloc_usable_size( locns[i] ) - 1] != '\0' ||
     167                         locns[i][malloc_size( locns[i] ) - 1] != '\0' ||
    173168                         ! malloc_zero_fill( locns[i] ) ) abort( "calloc/free corrupt storage5" );
    174169                locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last
     
    188183                for ( s; 1 ~ NoOfAllocs ) {                                             // allocation of size 0 can return null
    189184                        char * area = (char *)memalign( a, s );
    190                         if ( area == 0p ) abort( "memalign/free out of memory" );
    191185                        //sout | i | area;
    192186                        if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
     
    206200                        size_t s = i + default_mmap_start();            // cross over point
    207201                        char * area = (char *)memalign( a, s );
    208                         if ( area == 0p ) abort( "memalign/free out of memory" );
    209202                        //sout | i | area;
    210203                        if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
     
    217210        } // for
    218211
     212        // check malloc/resize/free (sbrk)
     213
     214        for ( i; 2 ~ NoOfAllocs ~ 12 ) {
     215                // initial N byte allocation
     216                char * area = (char *)malloc( i );
     217                area[0] = '\345'; area[i - 1] = '\345';                 // fill first/penultimate byte
     218
     219                // Do not start this loop index at 0 because resize of 0 bytes frees the storage.
     220                int prev = i;
     221                for ( s; i ~ 256 * 1024 ~ 26 ) {                                // start at initial memory request
     222                        if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/resize/free corrupt storage" );
     223                        area = (char *)resize( area, s );                       // attempt to reuse storage
     224                        area[0] = area[s - 1] = '\345';                         // fill last byte
     225                        prev = s;
     226                } // for
     227                free( area );
     228        } // for
     229
     230        // check malloc/resize/free (mmap)
     231
     232        for ( i; 2 ~ NoOfAllocs ~ 12 ) {
     233                // initial N byte allocation
     234                size_t s = i + default_mmap_start();                    // cross over point
     235                char * area = (char *)malloc( s );
     236                area[0] = '\345'; area[s - 1] = '\345';                 // fill first/penultimate byte
     237
     238                // Do not start this loop index at 0 because resize of 0 bytes frees the storage.
     239                int prev = s;
     240                for ( r; s ~ 256 * 1024 ~ 26 ) {                                // start at initial memory request
     241                        if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/resize/free corrupt storage" );
     242                        area = (char *)resize( area, s );                       // attempt to reuse storage
     243                        area[0] = area[r - 1] = '\345';                         // fill last byte
     244                        prev = r;
     245                } // for
     246                free( area );
     247        } // for
     248
     249        // check malloc/realloc/free (sbrk)
     250
     251        for ( i; 2 ~ NoOfAllocs ~ 12 ) {
     252                // initial N byte allocation
     253                char * area = (char *)malloc( i );
     254                area[0] = '\345'; area[i - 1] = '\345';                 // fill first/penultimate byte
     255
     256                // Do not start this loop index at 0 because realloc of 0 bytes frees the storage.
     257                int prev = i;
     258                for ( s; i ~ 256 * 1024 ~ 26 ) {                                // start at initial memory request
     259                        if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/realloc/free corrupt storage" );
     260                        area = (char *)realloc( area, s );                      // attempt to reuse storage
     261                        area[s - 1] = '\345';                                           // fill last byte
     262                        prev = s;
     263                } // for
     264                free( area );
     265        } // for
     266
     267        // check malloc/realloc/free (mmap)
     268
     269        for ( i; 2 ~ NoOfAllocs ~ 12 ) {
     270                // initial N byte allocation
     271                size_t s = i + default_mmap_start();                    // cross over point
     272                char * area = (char *)malloc( s );
     273                area[0] = '\345'; area[s - 1] = '\345';                 // fill first/penultimate byte
     274
     275                // Do not start this loop index at 0 because realloc of 0 bytes frees the storage.
     276                int prev = s;
     277                for ( r; s ~ 256 * 1024 ~ 26 ) {                                // start at initial memory request
     278                        if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/realloc/free corrupt storage" );
     279                        area = (char *)realloc( area, s );                      // attempt to reuse storage
     280                        area[r - 1] = '\345';                                           // fill last byte
     281                        prev = r;
     282                } // for
     283                free( area );
     284        } // for
     285
    219286        // check calloc/realloc/free (sbrk)
    220287
     
    222289                // initial N byte allocation
    223290                char * area = (char *)calloc( 5, i );
    224                 if ( area == 0p ) abort( "calloc/realloc/free out of memory" );
    225291                if ( area[0] != '\0' || area[i - 1] != '\0' ||
    226                          area[malloc_usable_size( area ) - 1] != '\0' ||
     292                         area[malloc_size( area ) - 1] != '\0' ||
    227293                         ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage1" );
    228294
     
    230296                for ( s; i ~ 256 * 1024 ~ 26 ) {                                // start at initial memory request
    231297                        area = (char *)realloc( area, s );                      // attempt to reuse storage
    232                         if ( area == 0p ) abort( "calloc/realloc/free out of memory" );
    233298                        if ( area[0] != '\0' || area[s - 1] != '\0' ||
    234                                  area[malloc_usable_size( area ) - 1] != '\0' ||
     299                                 area[malloc_size( area ) - 1] != '\0' ||
    235300                                 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage2" );
    236301                } // for
     
    244309                size_t s = i + default_mmap_start();                    // cross over point
    245310                char * area = (char *)calloc( 1, s );
    246                 if ( area == 0p ) abort( "calloc/realloc/free out of memory" );
    247311                if ( area[0] != '\0' || area[s - 1] != '\0' ||
    248                          area[malloc_usable_size( area ) - 1] != '\0' ||
    249                          ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage1" );
     312                         area[malloc_size( area ) - 1] != '\0' ||
     313                         ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage3" );
    250314
    251315                // Do not start this loop index at 0 because realloc of 0 bytes frees the storage.
    252316                for ( r; i ~ 256 * 1024 ~ 26 ) {                                // start at initial memory request
    253317                        area = (char *)realloc( area, r );                      // attempt to reuse storage
    254                         if ( area == 0p ) abort( "calloc/realloc/free out of memory" );
    255318                        if ( area[0] != '\0' || area[r - 1] != '\0' ||
    256                                  area[malloc_usable_size( area ) - 1] != '\0' ||
    257                                  ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage2" );
     319                                 area[malloc_size( area ) - 1] != '\0' ||
     320                                 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage4" );
    258321                } // for
    259322                free( area );
     
    266329                // initial N byte allocation
    267330                char * area = (char *)memalign( a, amount );    // aligned N-byte allocation
    268                 if ( area == 0p ) abort( "memalign/realloc/free out of memory" ); // no storage ?
    269331                //sout | alignments[a] | area;
    270332                if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
     
    277339                        if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "memalign/realloc/free corrupt storage" );
    278340                        area = (char *)realloc( area, s );                      // attempt to reuse storage
    279                         if ( area == 0p ) abort( "memalign/realloc/free out of memory" ); // no storage ?
    280341                        //sout | i | area;
    281342                        if ( (size_t)area % a != 0 ) {                          // check for initial alignment
     
    293354                for ( s; 1 ~ limit ) {                                                  // allocation of size 0 can return null
    294355                        char * area = (char *)cmemalign( a, 1, s );
    295                         if ( area == 0p ) abort( "cmemalign/free out of memory" );
    296356                        //sout | i | area;
    297357                        if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
     
    299359                        } // if
    300360                        if ( area[0] != '\0' || area[s - 1] != '\0' ||
    301                                  area[malloc_usable_size( area ) - 1] != '\0' ||
     361                                 area[malloc_size( area ) - 1] != '\0' ||
    302362                                 ! malloc_zero_fill( area ) ) abort( "cmemalign/free corrupt storage" );
    303363                        area[0] = '\345'; area[s - 1] = '\345';         // fill first/last byte
     
    312372                // initial N byte allocation
    313373                char * area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation
    314                 if ( area == 0p ) abort( "cmemalign/realloc/free out of memory" ); // no storage ?
    315374                //sout | alignments[a] | area;
    316375                if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
     
    318377                } // if
    319378                if ( area[0] != '\0' || area[amount - 1] != '\0' ||
    320                          area[malloc_usable_size( area ) - 1] != '\0' ||
     379                         area[malloc_size( area ) - 1] != '\0' ||
    321380                         ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage1" );
    322381                area[0] = '\345'; area[amount - 2] = '\345';    // fill first/penultimate byte
     
    326385                        if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "cmemalign/realloc/free corrupt storage2" );
    327386                        area = (char *)realloc( area, s );                      // attempt to reuse storage
    328                         if ( area == 0p ) abort( "cmemalign/realloc/free out of memory" ); // no storage ?
    329387                        //sout | i | area;
    330388                        if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
    331389                                abort( "cmemalign/realloc/free bad alignment %p", area );
    332390                        } // if
    333                         if ( area[s - 1] != '\0' || area[s - 1] != '\0' ||
    334                                  area[malloc_usable_size( area ) - 1] != '\0' ||
     391                        if ( area[0] != '\345' || area[s - 1] != '\0' ||
     392                                 area[malloc_size( area ) - 1] != '\0' ||
    335393                                 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage3" );
    336394                        area[s - 1] = '\345';                                           // fill last byte
     
    339397        } // for
    340398
     399        // check memalign/resize with align/free
     400
     401        amount = 2;
     402        for ( a; libAlign() ~= limit ~ a ) {                            // generate powers of 2
     403                // initial N byte allocation
     404                char * area = (char *)memalign( a, amount );    // aligned N-byte allocation
     405                //sout | alignments[a] | area | endl;
     406                if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
     407                        abort( "memalign/resize with align/free bad alignment : memalign(%d,%d) = %p", (int)a, (int)amount, area );
     408                } // if
     409                area[0] = '\345'; area[amount - 2] = '\345';    // fill first/penultimate byte
     410
     411                // Do not start this loop index at 0 because resize of 0 bytes frees the storage.
     412                for ( s; amount ~ 256 * 1024 ) {                                // start at initial memory request
     413                        area = (char *)resize( area, a * 2, s );        // attempt to reuse storage
     414                        //sout | i | area | endl;
     415                        if ( (size_t)area % a * 2 != 0 ) {                      // check for initial alignment
     416                                abort( "memalign/resize with align/free bad alignment %p", area );
     417                        } // if
     418                        area[s - 1] = '\345';                                           // fill last byte
     419                } // for
     420                free( area );
     421        } // for
     422
    341423        // check memalign/realloc with align/free
    342424
     
    345427                // initial N byte allocation
    346428                char * area = (char *)memalign( a, amount );    // aligned N-byte allocation
    347                 if ( area == 0p ) abort( "memalign/realloc with align/free out of memory" ); // no storage ?
    348429                //sout | alignments[a] | area | endl;
    349430                if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
     
    356437                        if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "memalign/realloc/free corrupt storage" );
    357438                        area = (char *)realloc( area, a * 2, s );       // attempt to reuse storage
    358                         if ( area == 0p ) abort( "memalign/realloc with align/free out of memory" ); // no storage ?
    359439                        //sout | i | area | endl;
    360440                        if ( (size_t)area % a * 2 != 0 ) {                      // check for initial alignment
     
    371451        for ( size_t a = libAlign() + libAlign(); a <= limit; a += a ) { // generate powers of 2
    372452                // initial N byte allocation
    373                 char *area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation
    374                 if ( area == 0p ) abort( "cmemalign/realloc with align/free out of memory" ); // no storage ?
     453                char * area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation
    375454                //sout | alignments[a] | area | endl;
    376455                if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment
     
    378457                } // if
    379458                if ( area[0] != '\0' || area[amount - 1] != '\0' ||
    380                          area[malloc_usable_size( area ) - 1] != '\0' ||
     459                         area[malloc_size( area ) - 1] != '\0' ||
    381460                         ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc with align/free corrupt storage1" );
    382461                area[0] = '\345'; area[amount - 2] = '\345';    // fill first/penultimate byte
     
    386465                        if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "cmemalign/realloc with align/free corrupt storage2" );
    387466                        area = (char *)realloc( area, a * 2, s );       // attempt to reuse storage
    388                         if ( area == 0p ) abort( "cmemalign/realloc with align/free out of memory" ); // no storage ?
    389467                        //sout | i | area | endl;
    390468                        if ( (size_t)area % a * 2 != 0 || malloc_alignment( area ) != a * 2 ) { // check for initial alignment
    391                                 abort( "cmemalign/realloc with align/free bad alignment %p %jd %jd", area, malloc_alignment( area ), a * 2 );
     469                                abort( "cmemalign/realloc with align/free bad alignment %p %zd %zd", area, malloc_alignment( area ), a * 2 );
    392470                        } // if
    393471                        if ( area[s - 1] != '\0' || area[s - 1] != '\0' ||
    394                                  area[malloc_usable_size( area ) - 1] != '\0' ||
     472                                 area[malloc_size( area ) - 1] != '\0' ||
    395473                                 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage3" );
    396474                        area[s - 1] = '\345';                                           // fill last byte
     
    410488        // checkFreeOn();
    411489        // malloc_stats();
     490        printf( "done\n" );                                                                     // non-empty .expect file
    412491}
    413492
  • tests/identFuncDeclarator.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed Aug 17 08:36:34 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Nov  6 17:56:33 2018
    13 // Update Count     : 3
     12// Last Modified On : Sun Sep 27 08:20:46 2020
     13// Update Count     : 5
    1414//
    1515
     
    111111        int (* (* const f80)(int))();
    112112        int (* const(* const f81)(int))();
     113
     114        #pragma message( "Compiled" )                   // force non-empty .expect file
    113115}
    114116
  • tests/identParamDeclarator.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed Aug 17 08:37:56 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Nov  6 17:56:44 2018
    13 // Update Count     : 3
     12// Last Modified On : Fri Sep 25 14:31:08 2020
     13// Update Count     : 4
    1414//
    1515
     
    158158
    159159int main( int argc, char const *argv[] ) {                              // dummy main
    160         return 0;
     160        printf( "done\n" );                                                                     // non-empty .expect file
    161161}
    162162
  • tests/io2.cfa

    r3c64c668 r58fe85a  
    121121
    122122        [int, int, const char *, double] t3 = { 3, 4, "a", 7.2 };
    123         sout | [ 3, 4, "a", 7.2 ];
     123        sout | [ 3, 4, (const char*)"a", 7.2 ];             // workaround trac#207: the const cast should not be needed
    124124        sout | t3;
    125125        sepSetTuple( sout, " " );
  • tests/labelledExit.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed Aug 10 07:29:39 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Feb  5 16:49:48 2020
    13 // Update Count     : 9
     12// Last Modified On : Sun Sep 27 09:01:34 2020
     13// Update Count     : 12
    1414//
    1515
     
    179179
    180180int main( int argc, char const *argv[] ) {
    181         /* code */
     181        #pragma message( "Compiled" )                                           // force non-empty .expect file
    182182}
    183183
  • tests/limits.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue May 10 20:44:20 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Nov  6 17:57:55 2018
    13 // Update Count     : 8
     12// Last Modified On : Sun Sep 27 08:45:43 2020
     13// Update Count     : 10
    1414//
     15
     16// Note: For testing the ability to load the constants defined in libcfa/src/limits.cfa,
     17// see discussion in test const-init.
    1518
    1619#include <limits.hfa>
     
    147150
    148151int main(int argc, char const *argv[]) {
    149         //DUMMY
    150         return 0;
     152        #pragma message( "Compiled" )                                           // force non-empty .expect file
    151153}
    152154
  • tests/linking/withthreads.cfa

    r3c64c668 r58fe85a  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // nothreads.cfa --
     7// withthreads.cfa --
    88//
    99// Author           : Thierry Delisle
  • tests/literals.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Sat Sep  9 16:34:38 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb 12 08:07:39 2019
    13 // Update Count     : 224
     12// Last Modified On : Sat Aug 29 10:57:56 2020
     13// Update Count     : 226
    1414//
    1515
     
    151151        -0X0123456789.0123456789P-09;  -0X0123456789.0123456789P-09f;  -0X0123456789.0123456789P-09l;  -0X0123456789.0123456789P-09F;  -0X0123456789.0123456789P-09L;
    152152
     153#if defined( __i386 ) || defined( __x86_64 )
    153154#if defined(__GNUC__) && __GNUC_PREREQ(7,0)                             // gcc version >= 7
    154155// floating with length, gcc f16/f128x unsupported and no prelude code for any _FloatXXx, so they work by conversion to long double
     
    194195        /* -0x123456789.0123456789P-09F16; */  -0x123456789.0123456789P-09F32;  -0x123456789.0123456789P-09F32x;  -0x123456789.0123456789P-09F64;  -0x123456789.0123456789P-09F64x;  -0x123456789.0123456789P-09W;  -0x123456789.0123456789P-09F128;  -0x123456789.0123456789P-09q;  /* -0x123456789.0123456789P-09q; */
    195196#endif // __GNUC_PREREQ(7,0)
     197#endif // __i386 ) || __x86_64
    196198
    197199#ifdef __CFA__
     
    214216        -01234567_l8;  -01234567_l16;  -01234567_l32;  -01234567_l64;  -01234567_l8u;  -01234567_ul16;  -01234567_l32u;  -01234567_ul64;
    215217
    216 #ifdef __LP64__ // 64-bit processor
     218#if defined( __SIZEOF_INT128__ )
    217219        01234567_l128;   01234567_ul128;
    218220        +01234567_l128;  +01234567_ul128;
    219221        -01234567_l128;  -01234567_ul128;
    220 #endif // __LP64__
     222#endif // __SIZEOF_INT128__
    221223
    222224        // decimal
     
    225227        -1234567890L8;  -1234567890L16;  -1234567890l32;  -1234567890l64;  -1234567890UL8;  -1234567890L16U;  -1234567890Ul32;  -1234567890l64u;
    226228
    227 #ifdef __LP64__ // 64-bit processor
     229#if defined( __SIZEOF_INT128__ )
    228230        1234567890l128;   1234567890l128u;
    229231        +1234567890l128;  +1234567890l128u;
    230232        -1234567890l128;  -1234567890l128u;
    231 #endif // __LP64__
     233    1234567890123456789_L128u; 1234567890123456789_L128u;
     234        18446708753438544741_l64u; 18446708753438544741_Ul64;
     235#endif // __SIZEOF_INT128__
    232236
    233237        // hexadecimal
  • tests/manipulatorsInput.cfa

    r3c64c668 r58fe85a  
    77// Created On       : Sat Jun  8 17:58:54 2019
    88// Last Modified By : Peter A. Buhr
    9 // Last Modified On : Thu Jun 13 17:41:43 2019
    10 // Update Count     : 37
     9// Last Modified On : Wed Jul 15 15:56:03 2020
     10// Update Count     : 47
    1111//
    1212
     
    152152                sin | ignore( wdi( 8, ldc ) );                  sout | ldc;
    153153        }
     154#if defined( __SIZEOF_INT128__ )
     155        {
     156                int128 val;
     157                for ( 15 ) {
     158                        sin | val;
     159                        sout | val;
     160                }
     161        }
     162#endif // __SIZEOF_INT128__
    154163} // main
    155164
  • tests/manipulatorsOutput1.cfa

    r3c64c668 r58fe85a  
    77// Created On       : Sat Jun  8 18:04:11 2019
    88// Last Modified By : Peter A. Buhr
    9 // Last Modified On : Mon Jun 10 12:37:28 2019
    10 // Update Count     : 8
     9// Last Modified On : Fri May  1 11:51:44 2020
     10// Update Count     : 9
    1111//
    1212
     
    1717        signed char sc = -12;
    1818        printf( "%hhd %2hhd %5.2hhd %-5.2hhd %hho %#hho %hhx %#hhx %#8hhx %#8.10hhx %#8.3hhX %+-8.3hhd %08hhd\n", sc, sc, sc, sc, sc, sc, sc, sc, sc, sc, sc, sc, sc );
    19         sout | sc | wd(2,sc) | wd(5,2,sc) | left(wd(5,2,sc)) | nobase(oct(sc)) | oct(sc) | nobase(hex(sc)) | hex(sc) | wd(8,hex(sc)) | wd(8,10,hex(sc)) | upcase(wd(8,3,hex(sc))) | left(sign(upcase(wd(8,3,sc)))) | pad0(wd(8,sc));
     19        sout | sc | wd(2,sc) | wd(5,2,sc) | left(wd(5,2,sc)) | nobase(oct(sc)) | oct(sc) | nonl;
     20        sout | nobase(hex(sc)) | hex(sc) | wd(8,hex(sc)) | wd(8,10,hex(sc)) | upcase(wd(8,3,hex(sc))) | nonl;
     21        sout | left(sign(upcase(wd(8,3,sc)))) | pad0(wd(8,sc));
    2022
    2123        sout | "unsigned char";
    2224        unsigned char usc = 12;
    2325        printf( "%hhu %2hhu %5.2hhu %-5.2hhu %hho %#hho %hhx %#hhx %#8hhx %#8.10hhx %#8.3hhX %-8.3hhu %08hhu\n", usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc );
    24         sout | usc | wd(2,usc) | wd(5,2,usc) | left(wd(5,2,usc)) | nobase(oct(usc)) | oct(usc) | nobase(hex(usc)) | hex(usc) | wd(8,hex(usc)) | wd(8,10,hex(usc)) | upcase(wd(8,3,hex(usc))) | left(upcase(wd(8,3,usc))) | pad0(wd(8,usc));
     26        sout | usc | wd(2,usc) | wd(5,2,usc) | left(wd(5,2,usc)) | nobase(oct(usc)) | oct(usc) | nonl;
     27        sout | nobase(hex(usc)) | hex(usc) | wd(8,hex(usc)) | wd(8,10,hex(usc)) | upcase(wd(8,3,hex(usc))) | nonl;
     28        sout | left(upcase(wd(8,3,usc))) | pad0(wd(8,usc));
    2529
    2630        sout | "signed short int";
    2731        signed short int si = -12;
    2832        printf( "%hd %2hd %5.2hd %-5.2hd %ho %#ho %hx %#hx %#8hx %#8.10hx %#8.3hX %+-8.3hd %08hd\n", si, si, si, si, si, si, si, si, si, si, si, si, si );
    29         sout | si | wd(2,si) | wd(5,2,si) | left(wd(5,2,si)) | nobase(oct(si)) | oct(si) | nobase(hex(si)) | hex(si) | wd(8,hex(si)) | wd(8,10,hex(si)) | upcase(wd(8,3,hex(si))) | left(sign(upcase(wd(8,3,si)))) | pad0(wd(8,si));
     33        sout | si | wd(2,si) | wd(5,2,si) | left(wd(5,2,si)) | nobase(oct(si)) | oct(si) | nonl;
     34        sout | nobase(hex(si)) | hex(si) | wd(8,hex(si)) | wd(8,10,hex(si)) | upcase(wd(8,3,hex(si))) | nonl;
     35        sout | left(sign(upcase(wd(8,3,si)))) | pad0(wd(8,si));
    3036
    3137        sout | "unsigned short int";
    3238        unsigned short int usi = 12;
    3339        printf( "%hu %2hu %5.2hu %-5.2hu %ho %#ho %hx %#hx %#8hx %#8.10hx %#8.3hX %-8.3hu %08hu\n", usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi );
    34         sout | usi | wd(2,usi) | wd(5,2,usi) | left(wd(5,2,usi)) | nobase(oct(usi)) | oct(usi) | nobase(hex(usi)) | hex(usi) | wd(8,hex(usi)) | wd(8,10,hex(usi)) | upcase(wd(8,3,hex(usi))) | left(upcase(wd(8,3,usi))) | pad0(wd(8,usi));
     40        sout | usi | wd(2,usi) | wd(5,2,usi) | left(wd(5,2,usi)) | nobase(oct(usi)) | oct(usi) | nonl;
     41        sout | nobase(hex(usi)) | hex(usi) | wd(8,hex(usi)) | wd(8,10,hex(usi)) | upcase(wd(8,3,hex(usi))) | nonl;
     42        sout | left(upcase(wd(8,3,usi))) | pad0(wd(8,usi));
    3543
    3644        sout | "signed int";
    3745        signed int i = -12;
    3846        printf( "%d %2d %5.2d %-5.2d %o %#o %x %#x %#8x %#8.10x %#8.3X %+-8.3d %08d\n", i, i, i, i, i, i, i, i, i, i, i, i, i );
    39         sout | i | wd(2,i) | wd(5,2,i) | left(wd(5,2,i)) | nobase(oct(i)) | oct(i) | nobase(hex(i)) | hex(i) | wd(8,hex(i)) | wd(8,10,hex(i)) | upcase(wd(8,3,hex(i))) | left(sign(upcase(wd(8,3,i)))) | pad0(wd(8,i));
     47        sout | i | wd(2,i) | wd(5,2,i) | left(wd(5,2,i)) | nobase(oct(i)) | oct(i) | nonl;
     48        sout | nobase(hex(i)) | hex(i) | wd(8,hex(i)) | wd(8,10,hex(i)) | upcase(wd(8,3,hex(i))) | nonl;
     49        sout | left(sign(upcase(wd(8,3,i)))) | pad0(wd(8,i));
    4050
    4151        sout | "unsigned int";
    4252        unsigned int ui = 12;
    4353        printf( "%u %2u %5.2u %-5.2u %o %#o %x %#x %#8x %#8.10x %#8.3X %-8.3u %08u\n", ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui );
    44         sout | ui | wd(2,ui) | wd(5,2,ui) | left(wd(5,2,ui)) | nobase(oct(ui)) | oct(ui) | nobase(hex(ui)) | hex(ui) | wd(8,hex(ui)) | wd(8,10,hex(ui)) | upcase(wd(8,3,hex(ui))) | left(upcase(wd(8,3,ui))) | pad0(wd(8,ui));
     54        sout | ui | wd(2,ui) | wd(5,2,ui) | left(wd(5,2,ui)) | nobase(oct(ui)) | oct(ui) | nonl;
     55        sout | nobase(hex(ui)) | hex(ui) | wd(8,hex(ui)) | wd(8,10,hex(ui)) | upcase(wd(8,3,hex(ui))) | nonl;
     56        sout | left(upcase(wd(8,3,ui))) | pad0(wd(8,ui));
    4557
    4658        sout | "signed long long int";
    4759        signed long long int lli = -12;
    4860        printf( "%lld %2lld %5.2lld %-5.2lld %llo %#llo %llx %#llx %#8llx %#8.10llx %#8.3llX %+-8.3lld %08lld\n", lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli );
    49         sout | lli | wd(2,lli) | wd(5,2,lli) | left(wd(5,2,lli)) | nobase(oct(lli)) | oct(lli) | nobase(hex(lli)) | hex(lli) | wd(8,hex(lli)) | wd(8,10,hex(lli)) | upcase(wd(8,3,hex(lli))) | left(sign(upcase(wd(8,3,lli)))) | pad0(wd(8,lli));
     61        sout | lli | wd(2,lli) | wd(5,2,lli) | left(wd(5,2,lli)) | nobase(oct(lli)) | oct(lli) | nonl;
     62        sout | nobase(hex(lli)) | hex(lli) | wd(8,hex(lli)) | wd(8,10,hex(lli)) | upcase(wd(8,3,hex(lli))) | nonl;
     63        sout | left(sign(upcase(wd(8,3,lli)))) | pad0(wd(8,lli));
    5064
    5165        sout | "unsigned long long int";
    5266        unsigned long long int ulli = 12;
    5367        printf( "%llu %2llu %5.2llu %-5.2llu %llo %#llo %llx %#llx %#8llx %#8.10llx %#8.3llX %-8.3llu %08llu\n", ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli );
    54         sout | ulli | wd(2,ulli) | wd(5,2,ulli) | left(wd(5,2,ulli)) | nobase(oct(ulli)) | oct(ulli) | nobase(hex(ulli)) | hex(ulli) | wd(8,hex(ulli)) | wd(8,10,hex(ulli)) | upcase(wd(8,3,hex(ulli))) | left(upcase(wd(8,3,ulli))) | pad0(wd(8,ulli));
     68        sout | ulli | wd(2,ulli) | wd(5,2,ulli) | left(wd(5,2,ulli)) | nobase(oct(ulli)) | oct(ulli) | nonl;
     69        sout | nobase(hex(ulli)) | hex(ulli) | wd(8,hex(ulli)) | wd(8,10,hex(ulli)) | upcase(wd(8,3,hex(ulli))) | nonl;
     70        sout | left(upcase(wd(8,3,ulli))) | pad0(wd(8,ulli));
    5571
    5672        sout | nl | "binary integral";
    57         sout | bin(0) | bin(13) | upcase(bin(13)) | nobase(bin(13)) | left(wd(8,bin(13))) | wd(8,bin(13)) | pad0(left(wd(8,bin(13)))) | pad0(wd(8,bin(13))) | pad0(wd(8,10,bin(13))) | pad0(wd(8,6,bin(13)));
     73        sout | bin(0) | bin(13) | upcase(bin(13)) | nobase(bin(13)) | left(wd(8,bin(13))) | wd(8,bin(13)) | nonl;
     74        sout | pad0(left(wd(8,bin(13)))) | pad0(wd(8,bin(13))) | pad0(wd(8,10,bin(13))) | pad0(wd(8,6,bin(13)));
    5875
    5976
     
    6279        printf( "%g  %8g %#8g %g %8g %8.0g %#8.0g %8.2g %#8.2g %-8.2g %-8.2g %-#8.2g %-+8.2g %-+#8.2g %08.2g %8.2E %8.2a %#8.2A %#8.2e\n",
    6380                    0.0,3.0F,3.0F, f,  f,    f,     f,    f,     f,  3.0F,      f,      f,      f,       f,     f,    f,    f,     f,     f );
    64         sout | 0.0 | wd(8, 3.0F) | nodp(wd(8, 3.0F)) | f | wd(8, f) | ws(8,0, f) | nodp(ws(8,0, f)) | ws(8,2, f) | nodp(ws(8,2, f)) | left(ws(8,2, 3.0F)) | left(ws(8,2, f)) | left(nodp(ws(8,2, f))) | left(sign(ws(8,2, f))) | left(sign(nodp(ws(8,2, f)))) | pad0(ws(8,2, f)) | upcase(wd(8,2, sci(f))) | wd(8,2, hex(f)) | upcase(wd(8,2, hex(f))) | nodp(wd(8,2, sci(f)));
     81        sout | 0.0 | wd(8, 3.0F) | nodp(wd(8, 3.0F)) | f | wd(8, f) | ws(8,0, f) | nodp(ws(8,0, f)) | ws(8,2, f) | nodp(ws(8,2, f)) | nonl;
     82        sout | left(ws(8,2, 3.0F)) | left(ws(8,2, f)) | left(nodp(ws(8,2, f))) | left(sign(ws(8,2, f))) | left(sign(nodp(ws(8,2, f)))) | nonl;
     83        sout | pad0(ws(8,2, f)) | upcase(wd(8,2, sci(f))) | wd(8,2, hex(f)) | upcase(wd(8,2, hex(f))) | nodp(wd(8,2, sci(f)));
    6584
    6685        sout | "double";
     
    6887        printf( "%g  %#8f %g %8f %#8.0f %8.0f %8.2f %-8.2f %-+#8.2f %08.2F %8.2E %8.2a %8.2A %8.2e\n",
    6988                        0.0,  3.0, d,  d,     d,    d,    d,     d,       d,     d,    d,    d,    d,    d );
    70         sout | 0.0 | wd(8, 3.0) | d | wd(8, d) | nodp(wd(8,0, d)) | wd(8,0, d) | wd(8,2, d) | left(wd(8,2, d)) | left(sign(wd(8,2, d))) | pad0(upcase(wd(8,2, d))) | upcase(wd(8,2, sci(d))) | wd(8,2, hex(d)) | upcase(wd(8,2, hex(d))) | wd(8,2, sci(d));
     89        sout | 0.0 | wd(8, 3.0) | d | wd(8, d) | nodp(wd(8,0, d)) | wd(8,0, d) | wd(8,2, d) | nonl;
     90        sout | left(wd(8,2, d)) | left(sign(wd(8,2, d))) | pad0(upcase(wd(8,2, d))) | upcase(wd(8,2, sci(d))) | wd(8,2, hex(d)) | upcase(wd(8,2, hex(d))) | wd(8,2, sci(d));
    7191
    7292        sout | "long double";
     
    7494        printf( "%Lg  %#8Lf %Lg %8Lf %#8.0Lf %8.0Lf %8.2Lf %-8.2Lf %-+#8.2Lf %08.2LF %8.2LE %8.2La %8.2LA %8.2Le\n",
    7595                        0.0L,  3.0L, ld,  ld,     ld,    ld,    ld,     ld,       ld,     ld,    ld,    ld,    ld,    ld );
    76         sout | 0.0L | wd(8, 3.0L) | ld | wd(8, ld) | nodp(wd(8,0, ld)) | wd(8,0, ld) | wd(8,2, ld) | left(wd(8,2, ld)) | left(sign(wd(8,2, ld))) | pad0(upcase(wd(8,2, ld))) | upcase(wd(8,2, sci(ld))) | wd(8,2, hex(ld)) | upcase(wd(8,2, hex(ld))) | wd(8,2, sci(ld));
     96        sout | 0.0L | wd(8, 3.0L) | ld | wd(8, ld) | nodp(wd(8,0, ld)) | wd(8,0, ld) | wd(8,2, ld) | nonl;
     97        sout | left(wd(8,2, ld)) | left(sign(wd(8,2, ld))) | pad0(upcase(wd(8,2, ld))) | upcase(wd(8,2, sci(ld))) | wd(8,2, hex(ld)) | upcase(wd(8,2, hex(ld))) | wd(8,2, sci(ld));
    7798
    7899
     
    80101        char c = 'a';
    81102        printf( "%c %2c %5c %-5c %hho %#hho %hhx %#hhx %#8hhx %#8hhX %-8c %8c\n", c, c, c, c, c, c, c, c, c, c, c, c );
    82         sout | c | ' ' | wd(2,c) | wd(5,c) | left(wd(5,c)) | nobase(oct(c)) | oct(c) | nobase(hex(c)) | hex(c) | wd(8,hex(c)) | upcase(wd(8,hex(c))) | left(wd(8,c)) | wd(8,c);
     103        sout | c | ' ' | wd(2,c) | wd(5,c) | left(wd(5,c)) | nobase(oct(c)) | oct(c) | nonl;
     104        sout | nobase(hex(c)) | hex(c) | wd(8,hex(c)) | upcase(wd(8,hex(c))) | left(wd(8,c)) | wd(8,c);
    83105
    84106        sout | nl | "string";
  • tests/manipulatorsOutput2.cfa

    r3c64c668 r58fe85a  
    77// Created On       : Sat Jun  8 18:04:11 2019
    88// Last Modified By : Peter A. Buhr
    9 // Last Modified On : Mon Jun 10 12:37:57 2019
    10 // Update Count     : 8
     9// Last Modified On : Sun Nov 15 08:11:53 2020
     10// Update Count     : 9
    1111//
    1212
     
    5252// Local Variables: //
    5353// tab-width: 4 //
    54 // compile-command: "cfa -Wall -Wextra amanipulatorsOutput2.cfa" //
     54// compile-command: "cfa -Wall -Wextra manipulatorsOutput2.cfa" //
    5555// End: //
  • tests/math4.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Thu May 24 20:56:54 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Dec  4 18:15:01 2018
    13 // Update Count     : 4
     12// Last Modified On : Tue Aug 25 17:56:45 2020
     13// Update Count     : 7
    1414//
    1515
     
    1818
    1919int main( void ) {
     20        signed char sc, scr1, scr2, scr3;
     21        unsigned char uc, ucr1, ucr2, ucr3;
     22        short int si, sir1, sir2, sir3;
     23        unsigned short int usi, usir1, usir2, usir3;
     24        int i, ir1, ir2, ir3;
     25        unsigned int ui, uir1, uir2, uir3;
     26        long int li, lir1, lir2, lir3;
     27        unsigned long int uli, ulir1, ulir2, ulir3;
     28        long long int lli, llir1, llir2, llir3;
     29        unsigned long long int ulli, ullir1, ullir2, ullir3;
     30
    2031        float f;
    2132        double d;
     
    2334
    2435        //---------------------- Nearest Integer ----------------------
     36
     37        //============================================================
     38#if 1
     39        sout | nl | "floor" | nl | nl;
     40
     41        printf( "signed char\n" );
     42        for ( sc = 1; sc != 0; sc <<= 1 ) {
     43                scr1 = floor( sc, sc ); scr2 = floor( sc + 2hh, sc ); scr3 = floor( -sc - 2hh, sc );
     44                printf( "floor(%hhd, %hhd) = %hhd, floor(%hhd, %hhd) = %hhd, floor(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 );
     45        } // for
     46        printf( "\n" );
     47
     48        printf( "unsigned char\n" );
     49        for ( uc = 1; uc != 0; uc <<= 1 ) {
     50                ucr1 = floor( uc, uc ); ucr2 = floor( uc + 2hh, uc ); ucr3 = floor( -uc - 2hh, uc );
     51                printf( "floor(%hhu, %hhu) = %hhu, floor(%hhu, %hhu) = %hhu, floor(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 );
     52        } // for
     53        printf( "\n" );
     54
     55        printf( "short int\n" );
     56        for ( si = 1; si != 0; si <<= 1 ) {
     57                sir1 = floor( si, si ); sir2 = floor( si + 2hh, si ); sir3 = floor( -si - 2hh, si );
     58                printf( "floor(%hd, %hd) = %hd, floor(%hd, %hd) = %hd, floor(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 );
     59        } // for
     60        printf( "\n" );
     61
     62        printf( "unsigned short int\n" );
     63        for ( usi = 1; usi != 0; usi <<= 1 ) {
     64                usir1 = floor( usi, usi ); usir2 = floor( usi + 2hh, usi ); usir3 = floor( -usi - 2hh, usi );
     65                printf( "floor(%hu, %hu) = %hu, floor(%hu, %hu) = %hu, floor(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 );
     66        } // for
     67        printf( "\n" );
     68
     69        printf( "int\n" );
     70        for ( i = 1; i != 0; i <<= 1 ) {
     71                ir1 = floor( i, i ); ir2 = floor( i + 2hh, i ); ir3 = floor( -i - 2hh, i );
     72                printf( "floor(%d, %d) = %d, floor(%d, %d) = %d, floor(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 );
     73        } // for
     74        printf( "\n" );
     75
     76        printf( "unsigned int\n" );
     77        for ( ui = 1; ui != 0; ui <<= 1 ) {
     78                uir1 = floor( ui, ui ); uir2 = floor( ui + 2hh, ui ); uir3 = floor( -ui - 2hh, ui );
     79                printf( "floor(%u, %u) = %u, floor(%u, %u) = %u, floor(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 );
     80        } // for
     81        printf( "\n" );
     82
     83        printf( "long int\n" );
     84        for ( li = 1; li != 0; li <<= 1 ) {
     85                lir1 = floor( li, li ); lir2 = floor( li + 2hh, li ); lir3 = floor( -li - 2hh, li );
     86                printf( "floor(%ld, %ld) = %ld, floor(%ld, %ld) = %ld, floor(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 );
     87        } // for
     88        printf( "\n" );
     89
     90        printf( "unsigned long int\n" );
     91        for ( uli = 1; uli != 0; uli <<= 1 ) {
     92                ulir1 = floor( uli, uli ); ulir2 = floor( uli + 2hh, uli ); ulir3 = floor( -uli - 2hh, uli );
     93                printf( "floor(%lu, %lu) = %lu, floor(%lu, %lu) = %lu, floor(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 );
     94        } // for
     95        printf( "\n" );
     96
     97        printf( "long long int\n" );
     98        for ( lli = 1; lli != 0; lli <<= 1 ) {
     99                llir1 = floor( lli, lli ); llir2 = floor( lli + 2hh, lli ); llir3 = floor( -lli - 2hh, lli );
     100                printf( "floor(%lld, %lld) = %lld, floor(%lld, %lld) = %lld, floor(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 );
     101        } // for
     102        printf( "\n" );
     103
     104        printf( "unsigned long long int\n" );
     105        for ( ulli = 1; ulli != 0; ulli <<= 1 ) {
     106                ullir1 = floor( ulli, ulli ); ullir2 = floor( ulli + 2hh, ulli ); ullir3 = floor( -ulli - 2hh, ulli );
     107                printf( "floor(%llu, %llu) = %llu, floor(%llu, %llu) = %llu, floor(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 );
     108        } // for
     109        printf( "\n" );
     110#endif // 0
     111        //============================================================
     112#if 1
     113        sout | nl | "ceiling_div" | nl | nl;
     114
     115        printf( "signed char\n" );
     116        for ( sc = 1; sc != 0; sc <<= 1 ) {
     117                scr1 = ceiling_div( sc, sc ); scr2 = ceiling_div( sc + 2hh, sc ); scr3 = ceiling_div( -sc - 2hh, sc );
     118                printf( "ceiling_div(%hhd, %hhd) = %hhd, ceiling_div(%hhd, %hhd) = %hhd, ceiling_div(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 );
     119        } // for
     120        printf( "\n" );
     121
     122        printf( "unsigned char\n" );
     123        for ( uc = 1; uc != 0; uc <<= 1 ) {
     124                ucr1 = ceiling_div( uc, uc ); ucr2 = ceiling_div( uc + 2hh, uc ); ucr3 = ceiling_div( -uc - 2hh, uc );
     125                printf( "ceiling_div(%hhu, %hhu) = %hhu, ceiling_div(%hhu, %hhu) = %hhu, ceiling_div(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 );
     126        } // for
     127        printf( "\n" );
     128
     129        printf( "short int\n" );
     130        for ( si = 1; si != 0; si <<= 1 ) {
     131                sir1 = ceiling_div( si, si ); sir2 = ceiling_div( si + 2hh, si ); sir3 = ceiling_div( -si - 2hh, si );
     132                printf( "ceiling_div(%hd, %hd) = %hd, ceiling_div(%hd, %hd) = %hd, ceiling_div(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 );
     133        } // for
     134        printf( "\n" );
     135
     136        printf( "unsigned short int\n" );
     137        for ( usi = 1; usi != 0; usi <<= 1 ) {
     138                usir1 = ceiling_div( usi, usi ); usir2 = ceiling_div( usi + 2hh, usi ); usir3 = ceiling_div( -usi - 2hh, usi );
     139                printf( "ceiling_div(%hu, %hu) = %hu, ceiling_div(%hu, %hu) = %hu, ceiling_div(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 );
     140        } // for
     141        printf( "\n" );
     142
     143        printf( "int\n" );
     144        for ( i = 1; i != 0; i <<= 1 ) {
     145                ir1 = ceiling_div( i, i ); ir2 = ceiling_div( i + 2hh, i ); ir3 = ceiling_div( -i - 2hh, i );
     146                printf( "ceiling_div(%d, %d) = %d, ceiling_div(%d, %d) = %d, ceiling_div(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 );
     147        } // for
     148        printf( "\n" );
     149
     150        printf( "unsigned int\n" );
     151        for ( ui = 1; ui != 0; ui <<= 1 ) {
     152                uir1 = ceiling_div( ui, ui ); uir2 = ceiling_div( ui + 2hh, ui ); uir3 = ceiling_div( -ui - 2hh, ui );
     153                printf( "ceiling_div(%u, %u) = %u, ceiling_div(%u, %u) = %u, ceiling_div(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 );
     154        } // for
     155        printf( "\n" );
     156
     157        printf( "long int\n" );
     158        for ( li = 1; li != 0; li <<= 1 ) {
     159                lir1 = ceiling_div( li, li ); lir2 = ceiling_div( li + 2hh, li ); lir3 = ceiling_div( -li - 2hh, li );
     160                printf( "ceiling_div(%ld, %ld) = %ld, ceiling_div(%ld, %ld) = %ld, ceiling_div(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 );
     161        } // for
     162        printf( "\n" );
     163
     164        printf( "unsigned long int\n" );
     165        for ( uli = 1; uli != 0; uli <<= 1 ) {
     166                ulir1 = ceiling_div( uli, uli ); ulir2 = ceiling_div( uli + 2hh, uli ); ulir3 = ceiling_div( -uli - 2hh, uli );
     167                printf( "ceiling_div(%lu, %lu) = %lu, ceiling_div(%lu, %lu) = %lu, ceiling_div(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 );
     168        } // for
     169        printf( "\n" );
     170
     171        printf( "long long int\n" );
     172        for ( lli = 1; lli != 0; lli <<= 1 ) {
     173                llir1 = ceiling_div( lli, lli ); llir2 = ceiling_div( lli + 2hh, lli ); llir3 = ceiling_div( -lli - 2hh, lli );
     174                printf( "ceiling_div(%lld, %lld) = %lld, ceiling_div(%lld, %lld) = %lld, ceiling_div(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 );
     175        } // for
     176        printf( "\n" );
     177
     178        printf( "unsigned long long int\n" );
     179        for ( ulli = 1; ulli != 0; ulli <<= 1 ) {
     180                ullir1 = ceiling_div( ulli, ulli ); ullir2 = ceiling_div( ulli + 2hh, ulli ); ullir3 = ceiling_div( -ulli - 2hh, ulli );
     181                printf( "ceiling_div(%llu, %llu) = %llu, ceiling_div(%llu, %llu) = %llu, ceiling_div(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 );
     182        } // for
     183        printf( "\n" );
     184#endif // 0
     185        //============================================================
     186#if 1
     187        sout | nl | "ceiling" | nl | nl;
     188
     189        printf( "signed char\n" );
     190        for ( sc = 1; sc != 0; sc <<= 1 ) {
     191                scr1 = ceiling( sc, sc ); scr2 = ceiling( sc + 2hh, sc ); scr3 = ceiling( -sc - 2hh, sc );
     192                printf( "ceiling(%hhd, %hhd) = %hhd, ceiling(%hhd, %hhd) = %hhd, ceiling(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 );
     193        } // for
     194        printf( "\n" );
     195
     196        printf( "unsigned char\n" );
     197        for ( uc = 1; uc != 0; uc <<= 1 ) {
     198                ucr1 = ceiling( uc, uc ); ucr2 = ceiling( uc + 2hh, uc ); ucr3 = ceiling( -uc - 2hh, uc );
     199                printf( "ceiling(%hhu, %hhu) = %hhu, ceiling(%hhu, %hhu) = %hhu, ceiling(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 );
     200        } // for
     201        printf( "\n" );
     202
     203        printf( "short int\n" );
     204        for ( si = 1; si != 0; si <<= 1 ) {
     205                sir1 = ceiling( si, si ); sir2 = ceiling( si + 2hh, si ); sir3 = ceiling( -si - 2hh, si );
     206                printf( "ceiling(%hd, %hd) = %hd, ceiling(%hd, %hd) = %hd, ceiling(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 );
     207        } // for
     208        printf( "\n" );
     209
     210        printf( "unsigned short int\n" );
     211        for ( usi = 1; usi != 0; usi <<= 1 ) {
     212                usir1 = ceiling( usi, usi ); usir2 = ceiling( usi + 2hh, usi ); usir3 = ceiling( -usi - 2hh, usi );
     213                printf( "ceiling(%hu, %hu) = %hu, ceiling(%hu, %hu) = %hu, ceiling(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 );
     214        } // for
     215        printf( "\n" );
     216
     217        printf( "int\n" );
     218        for ( i = 1; i != 0; i <<= 1 ) {
     219                ir1 = ceiling( i, i ); ir2 = ceiling( i + 2hh, i ); ir3 = ceiling( -i - 2hh, i );
     220                printf( "ceiling(%d, %d) = %d, ceiling(%d, %d) = %d, ceiling(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 );
     221        } // for
     222        printf( "\n" );
     223
     224        printf( "unsigned int\n" );
     225        for ( ui = 1; ui != 0; ui <<= 1 ) {
     226                uir1 = ceiling( ui, ui ); uir2 = ceiling( ui + 2hh, ui ); uir3 = ceiling( -ui - 2hh, ui );
     227                printf( "ceiling(%u, %u) = %u, ceiling(%u, %u) = %u, ceiling(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 );
     228        } // for
     229        printf( "\n" );
     230
     231        printf( "long int\n" );
     232        for ( li = 1; li != 0; li <<= 1 ) {
     233                lir1 = ceiling( li, li ); lir2 = ceiling( li + 2hh, li ); lir3 = ceiling( -li - 2hh, li );
     234                printf( "ceiling(%ld, %ld) = %ld, ceiling(%ld, %ld) = %ld, ceiling(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 );
     235        } // for
     236        printf( "\n" );
     237
     238        printf( "unsigned long int\n" );
     239        for ( uli = 1; uli != 0; uli <<= 1 ) {
     240                ulir1 = ceiling( uli, uli ); ulir2 = ceiling( uli + 2hh, uli ); ulir3 = ceiling( -uli - 2hh, uli );
     241                printf( "ceiling(%lu, %lu) = %lu, ceiling(%lu, %lu) = %lu, ceiling(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 );
     242        } // for
     243        printf( "\n" );
     244
     245        printf( "long long int\n" );
     246        for ( lli = 1; lli != 0; lli <<= 1 ) {
     247                llir1 = ceiling( lli, lli ); llir2 = ceiling( lli + 2hh, lli ); llir3 = ceiling( -lli - 2hh, lli );
     248                printf( "ceiling(%lld, %lld) = %lld, ceiling(%lld, %lld) = %lld, ceiling(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 );
     249        } // for
     250        printf( "\n" );
     251
     252        printf( "unsigned long long int\n" );
     253        for ( ulli = 1; ulli != 0; ulli <<= 1 ) {
     254                ullir1 = ceiling( ulli, ulli ); ullir2 = ceiling( ulli + 2hh, ulli ); ullir3 = ceiling( -ulli - 2hh, ulli );
     255                printf( "ceiling(%llu, %llu) = %llu, ceiling(%llu, %llu) = %llu, ceiling(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 );
     256        } // for
     257        printf( "\n" );
     258#endif // 0
    25259
    26260        sout | "floor:" | floor( 1.2F ) | floor( 1.2D ) | floor( 1.2L );
     
    69303// Local Variables: //
    70304// tab-width: 4 //
    71 // compile-command: "cfa math3.cfa" //
     305// compile-command: "cfa math4.cfa" //
    72306// End: //
  • tests/maybe.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Thr May 25 16:02:00 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Jul 20 15:24:07 2017
    13 // Update Count     : 1
     12// Last Modified On : Fri Sep 25 15:13:28 2020
     13// Update Count     : 2
    1414//
    1515
     
    6565        //checkNamedConstructors();
    6666        checkSetters();
     67        printf( "done\n" );                             // non-empty .expect file
    6768}
  • tests/minmax.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Dec  4 21:45:31 2018
    13 // Update Count     : 52
     12// Last Modified On : Sat Aug 15 08:28:01 2020
     13// Update Count     : 54
    1414//
    1515
     
    2323
    2424        sout | "char\t\t\t"                                     | 'z' | ' ' | 'a' | "\tmin " | min( 'z', 'a' );
    25         sout | "signed int\t\t"                         | 4 | 3 | "\tmin" | min( 4, 3 );
     25        sout | "signed int\t\t"                         | 4 | -3 | "\tmin" | min( 4, -3 );
    2626        sout | "unsigned int\t\t"                       | 4u | 3u | "\tmin" | min( 4u, 3u );
    27         sout | "signed long int\t\t"            | 4l | 3l | "\tmin" | min( 4l, 3l );
     27        sout | "signed long int\t\t"            | 4l | -3l | "\tmin" | min( 4l, -3l );
    2828        sout | "unsigned long int\t"            | 4ul | 3ul | "\tmin" | min( 4ul, 3ul );
    29         sout | "signed long long int\t"         | 4ll | 3ll | "\tmin" | min( 4ll, 3ll );
     29        sout | "signed long long int\t"         | 4ll | -3ll | "\tmin" | min( 4ll, -3ll );
    3030        sout | "unsigned long long int\t"       | 4ull | 3ull | "\tmin" | min( 4ull, 3ull );
    3131        sout | "float\t\t\t"                            | 4.0f | 3.1f | "\tmin" | min( 4.0f, 3.1f );
     
    3636
    3737        sout | "char\t\t\t"                                     | 'z' | ' ' | 'a' | "\tmax " | max( 'z', 'a' );
    38         sout | "signed int\t\t"                         | 4 | 3 | "\tmax" | max( 4, 3 );
     38        sout | "signed int\t\t"                         | 4 | -3 | "\tmax" | max( 4, -3 );
    3939        sout | "unsigned int\t\t"                       | 4u | 3u | "\tmax" | max( 4u, 3u );
    40         sout | "signed long int\t\t"            | 4l | 3l | "\tmax" | max( 4l, 3l );
     40        sout | "signed long int\t\t"            | 4l | -3l | "\tmax" | max( 4l, -3l );
    4141        sout | "unsigned long int\t"            | 4ul | 3ul | "\tmax" | max( 4ul, 3ul );
    42         sout | "signed long long int\t"         | 4ll | 3ll | "\tmax" | max( 4ll, 3ll );
     42        sout | "signed long long int\t"         | 4ll | -3ll | "\tmax" | max( 4ll, -3ll );
    4343        sout | "unsigned long long int\t"       | 4ull | 3ull | "\tmax" | max( 4ull, 3ull );
    4444        sout | "float\t\t\t"                            | 4.0f | 3.1f | "\tmax" | max( 4.0f, 3.1f );
  • tests/nested-types.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Mon Jul 9 10:20:03 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Feb 12 18:21:15 2020
    13 // Update Count     : 3
     12// Last Modified On : Sun Sep 27 08:48:59 2020
     13// Update Count     : 6
    1414//
    1515
    1616typedef int N;
    1717struct A {
    18   forall(otype T)
    19   struct N {
    20     T x;
    21   };
     18        forall(otype T)
     19        struct N {
     20                T x;
     21        };
    2222};
    2323
    2424struct S {
    25   struct T {
    26     int i;
    27     typedef int Bar;
    28   };
    29   T x;
     25        struct T {
     26                int i;
     27                typedef int Bar;
     28        };
     29        T x;
    3030
    31   // struct U;
    32   typedef T Bar;
    33   typedef int Baz;
     31        // struct U;
     32        typedef T Bar;
     33        typedef int Baz;
    3434};
    3535
     
    6565
    6666int main() {
    67   // access nested struct
    68   S.T x;
     67        // access nested struct
     68        S.T x;
    6969
    70   {
    71     struct S {
    72       int i;
    73       struct Z {
    74         double d;
    75       };
    76     };
     70        {
     71                struct S {
     72                  int i;
     73                  struct Z {
     74                    double d;
     75                  };
     76                };
    7777
    78     S.Z z;   // gets local S
    79     .S.T y;  // lookup at global scope only
     78                S.Z z;                                                                                  // gets local S
     79                .S.T y;                                                                                 // lookup at global scope only
    8080
    81     const volatile .S.T q;
     81                const volatile .S.T q;
    8282#if ERR1
    83     T err1;           // error: no T in scope
     83                T err1;                                                                                 // error: no T in scope
    8484#endif
    8585#if ERR2
    86     .Z err2;          // error: no Z in global scope
    87     .S.Baz.Bar err3;  // error: .S.Baz => int, int is not aggregate and should not appear left of the dot
    88     .S.Z err4;        // error: no Z in global S
     86                .Z err2;                                                                                // error: no Z in global scope
     87                .S.Baz.Bar err3;                                                                // error: .S.Baz => int, int is not aggregate and should not appear left of the dot
     88                .S.Z err4;                                                                              // error: no Z in global S
    8989#endif
    90   }
     90        }
    9191
    92   // U.S un;
     92        // U.S un;
    9393
    94   S.Bar y;
    95   S.Baz x;
    96   S.T.Bar z;
     94        S.Bar y;
     95        S.Baz x;
     96        S.T.Bar z;
    9797
    98   // A.N(int) x;  // xxx - should not be an error, but currently is.
     98        // A.N(int) x;  // xxx - should not be an error, but currently is.
     99
     100        #pragma message( "Compiled" )                   // force non-empty .expect file
    99101}
    100102
  • tests/numericConstants.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed May 24 22:10:36 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  5 08:58:16 2019
    13 // Update Count     : 5
     12// Last Modified On : Sun Sep 27 07:55:22 2020
     13// Update Count     : 7
    1414//
    1515
     
    6363        0x_ff.ffp0;                                     // hex real
    6464        0x_1.ffff_ffff_p_128_l;
     65
     66        #pragma message( "Compiled" )   // force non-empty .expect file
    6567} // main
    6668
  • tests/operators.cfa

    r3c64c668 r58fe85a  
    3131int main(int argc, char const *argv[]) {
    3232        /* code */
    33         return 0;
     33        printf( "done\n" );                             // non-empty .expect file
    3434}
    3535
  • tests/pybin/settings.py

    r3c64c668 r58fe85a  
    2323class Architecture:
    2424        KnownArchitectures = {
    25                 'x64'           : 'x64',
    26                 'x86-64'        : 'x64',
    27                 'x86_64'        : 'x64',
    28                 'x86'           : 'x86',
    29                 'aarch64'       : 'arm',
    30                 'i386'          : 'x86',
    31                 'i486'          : 'x86',
    32                 'i686'          : 'x86',
    33                 'Intel 80386'   : 'x86',
    34                 'arm'           : 'arm',
    35                 'ARM'           : 'arm',
     25                'x64'         : 'x64',
     26                'x86-64'      : 'x64',
     27                'x86_64'      : 'x64',
     28                'x86'         : 'x86',
     29                'aarch64'     : 'arm64',
     30                'arm64'       : 'arm64',
     31                'ARM64'       : 'arm64',
     32                'i386'        : 'x86',
     33                'i486'        : 'x86',
     34                'i686'        : 'x86',
     35                'Intel 80386' : 'x86',
     36                'arm'         : 'arm32',
     37                'ARM'         : 'arm32',
     38                'arm32'       : 'arm32',
     39                'ARM32'       : 'arm32',
    3640        }
    3741
    3842        CrossCompileFlags = {
    39                 'x64' : 'ARCH_FLAGS=-m64',
    40                 'x86' : 'ARCH_FLAGS=-m32',
     43                'x64'  : 'ARCH_FLAGS=-m64',
     44                'x86'  : 'ARCH_FLAGS=-m32',
     45                'arm64': 'ARCH_FLAGS=',
     46                'arm32': 'ARCH_FLAGS=',
    4147        }
    4248
     
    7783                        print("updated to %s" % self.target)
    7884
    79         def match(self, arch):
    80                 return True if not arch else self.target == arch
    81 
    82         @classmethod
    83         def make_canonical(_, arch):
     85        def filter(self, tests):
     86                return [test for test in tests if not test.arch or self.target == test.arch]
     87
     88        @staticmethod
     89        def make_canonical(arch):
    8490                return Architecture.KnownArchitectures[arch]
    8591
     
    9197                self.path   = "debug" if value else "nodebug"
    9298
     99class AST:
     100        def __init__(self, ast):
     101                if ast == "new":
     102                        self.target = ast
     103                        self.string = "New AST"
     104                        self.flags  = """AST_FLAGS=-XCFA,--new-ast"""
     105                elif ast == "old":
     106                        self.target = ast
     107                        self.string = "Old AST"
     108                        self.flags  = """AST_FLAGS=-XCFA,--old-ast"""
     109                elif ast == None:
     110                        self.target = "new" if config.NEWAST else "old"
     111                        self.string = "Default AST (%s)" % self.target
     112                        self.flags  = """AST_FLAGS="""
     113                else:
     114                        print("""ERROR: Invalid ast configuration, must be "old", "new" or left unspecified, was %s""" % (value), file=sys.stderr)
     115                        sys.exit(1)
     116
     117        def filter(self, tests):
     118
     119                return [test for test in tests if not test.astv or self.target == test.astv]
     120
    93121class Install:
    94122        def __init__(self, value):
     
    104132                self.total  = Timeouts.check(tg)
    105133
    106         @classmethod
    107         def check(_, value):
     134        @staticmethod
     135        def check(value):
    108136                if value < 1:
    109137                        print("Timeouts must be at least 1 second", file=sys.stderr)
     
    113141
    114142def init( options ):
     143        global all_ast
     144        global all_arch
     145        global all_debug
     146        global all_install
     147        global ast
    115148        global arch
     149        global debug
    116150        global archive
    117         global debug
    118         global distcc
     151        global install
     152
     153        global continue_
    119154        global dry_run
    120155        global generating
    121         global install
    122156        global make
    123157        global output_width
    124158        global timeout
    125 
    126         arch         = Architecture(options.arch)
     159        global timeout2gdb
     160
     161        all_ast      = [AST(o)          for o in list(dict.fromkeys(options.ast    ))] if options.ast  else [AST(None)]
     162        all_arch     = [Architecture(o) for o in list(dict.fromkeys(options.arch   ))] if options.arch else [Architecture(None)]
     163        all_debug    = [Debug(o)        for o in list(dict.fromkeys(options.debug  ))]
     164        all_install  = [Install(o)      for o in list(dict.fromkeys(options.install))]
    127165        archive      = os.path.abspath(os.path.join(original_path, options.archive_errors)) if options.archive_errors else None
    128         debug        = Debug(options.debug)
     166        continue_    = options.continue_
    129167        dry_run      = options.dry_run # must be called before tools.config_hash()
    130         distcc       = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash()
    131168        generating   = options.regenerate_expected
    132         install      = Install(options.install)
    133169        make         = ['make']
    134170        output_width = 24
    135171        timeout      = Timeouts(options.timeout, options.global_timeout)
     172        timeout2gdb  = options.timeout_with_gdb
    136173
    137174        # if we distribute, distcc errors will fail tests, use log file for distcc
     
    146183
    147184def validate():
     185        """Validate the current configuration and update globals"""
     186
     187        global distcc
     188        distcc       = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash()
    148189        errf = os.path.join(BUILDDIR, ".validate.err")
    149190        make_ret, out = tools.make( ".validate", error_file = errf, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL )
  • tests/pybin/test_run.py

    r3c64c668 r58fe85a  
    1111                self.path = ''
    1212                self.arch = ''
     13                self.astv = ''
    1314
    1415        def toString(self):
    15                 return "{:25s} ({:5s} {:s})".format( self.name, self.arch if self.arch else "Any", self.target() )
     16                return "{:25s} ({:5s} arch, {:s} ast: {:s})".format( self.name, self.arch if self.arch else "Any", self.astv if self.astv else "Any", self.target() )
    1617
    1718        def prepare(self):
     
    2021
    2122        def expect(self):
    22                 return os.path.normpath( os.path.join(settings.SRCDIR  , self.path, ".expect", "%s%s.txt" % (self.name,'' if not self.arch else ".%s" % self.arch)) )
     23                arch = '' if not self.arch else ".%s" % self.arch
     24                astv = '' if not self.astv else ".nast" if self.astv == "new" else ".oast"
     25                return os.path.normpath( os.path.join(settings.SRCDIR  , self.path, ".expect", "%s%s%s.txt" % (self.name,astv,arch)) )
    2326
    2427        def error_log(self):
     
    4043                return os.path.normpath( os.path.join(settings.BUILDDIR, self.path, self.name) )
    4144
    42         @classmethod
    43         def valid_name(_, name):
     45        @staticmethod
     46        def valid_name(name):
    4447                return not name.endswith( ('.c', '.cc', '.cpp', '.cfa') )
    4548
    46         @classmethod
    47         def from_target(_, target):
     49        @staticmethod
     50        def new_target(target, arch, astv):
    4851                test = Test()
    4952                test.name = os.path.basename(target)
    5053                test.path = os.path.relpath (os.path.dirname(target), settings.SRCDIR)
    51                 test.arch = settings.arch.target if settings.arch.cross_compile else ''
     54                test.arch = arch.target if arch else ''
     55                test.astv = astv.target if astv else ''
    5256                return test
    5357
     
    6973                        else :                                          text = "FAILED with code %d" % retcode
    7074
    71                 text += "    C%s - R%s" % (cls.fmtDur(duration[0]), cls.fmtDur(duration[1]))
     75                text += "    C%s - R%s" % (fmtDur(duration[0]), fmtDur(duration[1]))
    7276                return text
    73 
    74         @classmethod
    75         def fmtDur( cls, duration ):
    76                 if duration :
    77                         hours, rem = divmod(duration, 3600)
    78                         minutes, rem = divmod(rem, 60)
    79                         seconds, millis = divmod(rem, 1)
    80                         return "%2d:%02d.%03d" % (minutes, seconds, millis * 1000)
    81                 return " n/a"
  • tests/pybin/tools.py

    r3c64c668 r58fe85a  
    7373                                        )
    7474
    75                                         return proc.returncode, out.decode("utf-8") if out else None
     75                                        return proc.returncode, out.decode("latin-1") if out else None
    7676                                except subprocess.TimeoutExpired:
    77                                         proc.send_signal(signal.SIGABRT)
    78                                         proc.communicate()
    79                                         return 124, str(None)
     77                                        if settings.timeout2gdb:
     78                                                print("Process {} timeout".format(proc.pid))
     79                                                proc.communicate()
     80                                                return 124, str(None)
     81                                        else:
     82                                                proc.send_signal(signal.SIGABRT)
     83                                                proc.communicate()
     84                                                return 124, str(None)
    8085
    8186        except Exception as ex:
     
    8388                raise
    8489
     90def is_empty(fname):
     91        if not os.path.isfile(fname):
     92                return True
     93
     94        if os.stat(fname).st_size == 0:
     95                return True
     96
     97        return False
     98
    8599def is_ascii(fname):
    86100        if settings.dry_run:
    87101                print("is_ascii: %s" % fname)
    88                 return True
     102                return (True, "")
    89103
    90104        if not os.path.isfile(fname):
    91                 return False
    92 
    93         code, out = sh("file %s" % fname, output_file=subprocess.PIPE)
     105                return (False, "No file")
     106
     107        code, out = sh("file", fname, output_file=subprocess.PIPE)
    94108        if code != 0:
    95                 return False
     109                return (False, "'file EXPECT' failed with code {}".format(code))
    96110
    97111        match = re.search(".*: (.*)", out)
    98112
    99113        if not match:
    100                 return False
    101 
    102         return match.group(1).startswith("ASCII text")
     114                return (False, "Unreadable file type: '{}'".format(out))
     115
     116        if "ASCII text" in match.group(1):
     117                return (True, "")
     118
     119        return (False, "File type should be 'ASCII text', was '{}'".format(match.group(1)))
    103120
    104121def is_exe(fname):
     
    115132                return None
    116133
    117         file = open(file, mode)
     134        file = open(file, mode, encoding="latin-1") # use latin-1 so all chars mean something.
    118135        exitstack.push(file)
    119136        return file
     
    164181                '-s' if silent else None,
    165182                test_param,
     183                settings.ast.flags,
    166184                settings.arch.flags,
    167185                settings.debug.flags,
     
    173191        cmd = [s for s in cmd if s]
    174192        return sh(*cmd, output_file=output_file, error=error)
     193
     194def make_recon(target):
     195        cmd = [
     196                *settings.make,
     197                '-W',
     198                os.path.abspath(os.path.join(settings.BUILDDIR, '../driver/cfa')),
     199                '--recon',
     200                target
     201        ]
     202        cmd = [s for s in cmd if s]
     203        return sh(*cmd, output_file=subprocess.PIPE)
    175204
    176205def which(program):
     
    233262# helper function to check if a files contains only a specific string
    234263def file_contains_only(file, text) :
    235         with open(file) as f:
     264        with open(file, encoding="latin-1") as f: # use latin-1 so all chars mean something.
    236265                ff = f.read().strip()
    237266                result = ff == text.strip()
     
    241270# transform path to canonical form
    242271def canonical_path(path):
    243         abspath = os.path.abspath(__main__.__file__)
     272        abspath = os.path.abspath(os.path.realpath(__main__.__file__))
    244273        dname = os.path.dirname(abspath)
    245274        return os.path.join(dname, os.path.normpath(path) )
     
    322351        raise argparse.ArgumentTypeError(msg)
    323352
     353# Convert a function that converts a string to one that converts comma separated string.
     354def comma_separated(elements):
     355    return lambda string: [elements(part) for part in string.split(',')]
     356
    324357def fancy_print(text):
    325358        column = which('column')
     
    378411                while True:
    379412                        yield i.next(max(expire - time.time(), 0))
     413
     414def fmtDur( duration ):
     415        if duration :
     416                hours, rem = divmod(duration, 3600)
     417                minutes, rem = divmod(rem, 60)
     418                seconds, millis = divmod(rem, 1)
     419                return "%2d:%02d.%03d" % (minutes, seconds, millis * 1000)
     420        return " n/a"
  • tests/raii/.expect/ctor-autogen.txt

    r3c64c668 r58fe85a  
     1done
  • tests/raii/.expect/init_once.txt

    r3c64c668 r58fe85a  
     1done
  • tests/raii/ctor-autogen.cfa

    r3c64c668 r58fe85a  
    151151        identity(gcs);
    152152        identity(gcu);
     153        printf( "done\n" );                             // non-empty .expect file
    153154}
  • tests/raii/init_once.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Jun 14 15:43:35 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Mar 22 13:41:26 2019
    13 // Update Count     : 4
     12// Last Modified On : Fri Sep 25 15:36:39 2020
     13// Update Count     : 5
    1414//
    1515
     
    188188                static_variable();
    189189        }
     190        printf( "done\n" );                                                                     // non-empty .expect file
    190191}
    191192
  • tests/references.cfa

    r3c64c668 r58fe85a  
    124124                int *p = &a;
    125125                asm (
    126                         "incl %[p]\n\t"
    127                         : [p] "+m" (*p)
     126                        #if defined( __i386 ) || defined( __x86_64 )
     127                                "incl %[p]\n\t"
     128                                : [p] "+m" (*p)
     129                        #elif defined( __aarch64__ )
     130                                "ldr     w1, %[p]\n\t"
     131                                "add     w1, w1, 1\n\t"
     132                                "str     w1, %[p]\n\t"
     133                                : [p] "+m" (*p) ::"w1"
     134                        #endif
    128135                );
    129136                printf("%d\n", a);
  • tests/result.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Thr May 25 16:50:00 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Jul 20 15:24:12 2017
    13 // Update Count     : 1
     12// Last Modified On : Fri Sep 25 15:22:59 2020
     13// Update Count     : 2
    1414//
    1515
     
    6666        checkGetters();
    6767        checkSetters();
     68        printf( "done\n" );                             // non-empty .expect file
    6869}
  • tests/searchsort.cfa

    r3c64c668 r58fe85a  
    3838        } // for
    3939        sout | nl;
    40         for ( i; 0 ~ size ) {           // C version
     40        for ( i; 0 ~ size ) {           // C version, returns void*
    4141                int key = size - i;
    42                 int * v = bsearch( &key, iarr, size, sizeof( iarr[0] ), comp );
     42                int * v = ( int * ) bsearch( &key, iarr, size, sizeof( iarr[0] ), comp );
    4343                sout | key | ':' | *v | ", ";
    4444        } // for
  • tests/stdincludes.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Aug 29 08:26:14 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Nov  6 18:00:53 2018
    13 // Update Count     : 6
     12// Last Modified On : Sun Sep 27 08:51:38 2020
     13// Update Count     : 8
    1414//
    1515
     
    4747#include <wctype.h>
    4848
    49 int main() {}
     49int main() {
     50        #pragma message( "Compiled" )                   // force non-empty .expect file
     51}
    5052
    5153// Local Variables: //
  • tests/switch.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Jul 12 06:50:22 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Nov  6 18:01:34 2018
    13 // Update Count     : 37
     12// Last Modified On : Sun Sep 27 08:35:02 2020
     13// Update Count     : 43
    1414//
    1515
     
    100100                j = 5;
    101101        } // choose
     102
     103        #pragma message( "Compiled" )                                           // force non-empty .expect file
    102104} // main
    103105
  • tests/test.py

    r3c64c668 r58fe85a  
    66
    77import argparse
     8import itertools
    89import re
    910import sys
     
    2324
    2425        def match_test(path):
    25                 match = re.search("^%s\/([\w\/\-_]*).expect\/([\w\-_]+)(\.[\w\-_]+)?\.txt$" % settings.SRCDIR, path)
     26                match = re.search("^%s\/([\w\/\-_]*).expect\/([\w\-_]+)(\.nast|\.oast)?(\.[\w\-_]+)?\.txt$" % settings.SRCDIR, path)
    2627                if match :
    2728                        test = Test()
    2829                        test.name = match.group(2)
    2930                        test.path = match.group(1)
    30                         test.arch = match.group(3)[1:] if match.group(3) else None
    31                         if settings.arch.match(test.arch):
    32                                 expected.append(test)
     31                        test.arch = match.group(4)[1:] if match.group(4) else None
     32
     33                        astv = match.group(3)[1:] if match.group(3) else None
     34                        if astv == 'oast':
     35                                test.astv = 'old'
     36                        elif astv == 'nast':
     37                                test.astv = 'new'
     38                        elif astv:
     39                                print('ERROR: "%s", expect file has astv but it is not "nast" or "oast"' % testname, file=sys.stderr)
     40                                sys.exit(1)
     41
     42                        expected.append(test)
    3343
    3444        path_walk( match_test )
     
    5363                ]
    5464
     65        # sort the test alphabetically for convenience
     66        test_list.sort(key=lambda t: ('~' if t.arch else '') + t.target() + (t.arch if t.arch else ''))
     67
    5568        return test_list
    5669
     
    6376        if options.regenerate_expected :
    6477                for testname in options.tests :
    65                         testname = canonical_path( testname )
     78                        testname = os.path.normpath( os.path.join(settings.SRCDIR, testname) )
     79
     80                        # first check if this is a valid name to regenerate
    6681                        if Test.valid_name(testname):
     82                                # this is a valid name, let's check if it already exists
    6783                                found = [test for test in all_tests if canonical_path( test.target() ) == testname]
    68                                 tests.append( found[0] if len(found) == 1 else Test.from_target(testname) )
     84                                setup = itertools.product(settings.all_arch if options.arch else [None], settings.all_ast if options.ast else [None])
     85                                if not found:
     86                                        # it's a new name, create it according to the name and specified architecture/ast version
     87                                        tests.extend( [Test.new_target(testname, arch, ast) for arch, ast in setup] )
     88                                elif len(found) == 1 and not found[0].arch:
     89                                        # we found a single test, the user better be wanting to create a cross platform test
     90                                        if options.arch:
     91                                                print('ERROR: "%s", test has no specified architecture but --arch was specified, ignoring it' % testname, file=sys.stderr)
     92                                        elif options.ast:
     93                                                print('ERROR: "%s", test has no specified ast version but --ast was specified, ignoring it' % testname, file=sys.stderr)
     94                                        else:
     95                                                tests.append( found[0] )
     96                                else:
     97                                        # this test is already cross platform, just add a test for each platform the user asked
     98                                        tests.extend( [Test.new_target(testname, arch, ast) for arch, ast in setup] )
     99
     100                                        # print a warning if it users didn't ask for a specific architecture
     101                                        found_arch = [f.arch for f in found if f.arch]
     102                                        if found_arch and not options.arch:
     103                                                print('WARNING: "%s", test has architecture specific expected files but --arch was not specified, regenerating only for current host' % testname, file=sys.stderr)
     104
     105
     106                                        # print a warning if it users didn't ask for a specific ast version
     107                                        found_astv = [f.astv for f in found if f.astv]
     108                                        if found_astv and not options.ast:
     109                                                print('WARNING: "%s", test has ast version specific expected files but --ast was not specified, regenerating only for current ast' % testname, file=sys.stderr)
     110
    69111                        else :
    70112                                print('ERROR: "%s", tests are not allowed to end with a C/C++/CFA extension, ignoring it' % testname, file=sys.stderr)
     
    76118
    77119                        if test :
    78                                 tests.append( test[0] )
     120                                tests.extend( test )
    79121                        else :
    80122                                print('ERROR: No expected file for test %s, ignoring it' % testname, file=sys.stderr)
     
    86128        # create a parser with the arguments for the tests script
    87129        parser = argparse.ArgumentParser(description='Script which runs cforall tests')
    88         parser.add_argument('--debug', help='Run all tests in debug or release', type=yes_no, default='yes')
    89         parser.add_argument('--install', help='Run all tests based on installed binaries or tree binaries', type=yes_no, default='no')
    90         parser.add_argument('--arch', help='Test for specific architecture', type=str, default='')
    91         parser.add_argument('--timeout', help='Maximum duration in seconds after a single test is considered to have timed out', type=int, default=60)
     130        parser.add_argument('--ast', help='Test for specific ast', type=comma_separated(str), default=None)
     131        parser.add_argument('--arch', help='Test for specific architecture', type=comma_separated(str), default=None)
     132        parser.add_argument('--debug', help='Run all tests in debug or release', type=comma_separated(yes_no), default='yes')
     133        parser.add_argument('--install', help='Run all tests based on installed binaries or tree binaries', type=comma_separated(yes_no), default='no')
     134        parser.add_argument('--continue', help='When multiple specifications are passed (debug/install/arch), sets whether or not to continue if the last specification failed', type=yes_no, default='yes', dest='continue_')
     135        parser.add_argument('--timeout', help='Maximum duration in seconds after a single test is considered to have timed out', type=int, default=120)
    92136        parser.add_argument('--global-timeout', help='Maximum cumulative duration in seconds after the ALL tests are considered to have timed out', type=int, default=7200)
     137        parser.add_argument('--timeout-with-gdb', help='Instead of killing the command when it times out, orphan it and print process id to allow gdb to attach', type=yes_no, default="no")
    93138        parser.add_argument('--dry-run', help='Don\'t run the tests, only output the commands', action='store_true')
    94139        parser.add_argument('--list', help='List all test available', action='store_true')
     
    98143        parser.add_argument('-j', '--jobs', help='Number of tests to run simultaneously', type=int)
    99144        parser.add_argument('--list-comp', help='List all valide arguments', action='store_true')
     145        parser.add_argument('--list-dist', help='List all tests for distribution', action='store_true')
    100146        parser.add_argument('-I','--include', help='Directory of test to include, can be used multiple time, All  if omitted', action='append')
    101147        parser.add_argument('-E','--exclude', help='Directory of test to exclude, can be used multiple time, None if omitted', action='append')
     
    110156
    111157        # script must have at least some tests to run or be listing
    112         listing    = options.list or options.list_comp
     158        listing    = options.list or options.list_comp or options.list_dist
    113159        all_tests  = options.all
    114160        some_tests = len(options.tests) > 0
     
    145191        test.prepare()
    146192
     193        # ----------
     194        # MAKE
     195        # ----------
    147196        # build, skipping to next test on error
    148197        with Timed() as comp_dur:
    149198                make_ret, _ = make( test.target(), output_file=subprocess.DEVNULL, error=out_file, error_file = err_file )
    150199
     200        # ----------
     201        # RUN
     202        # ----------
     203        # run everything in a temp directory to make sure core file are handled properly
    151204        run_dur = None
    152         # run everything in a temp directory to make sure core file are handled properly
    153205        with tempdir():
    154206                # if the make command succeeds continue otherwise skip to diff
     
    166218                if success(retcode):
    167219                        if settings.generating :
    168                                 # if we are ounly generating the output we still need to check that the test actually exists
     220                                # if we are only generating the output we still need to check that the test actually exists
    169221                                if no_rule(out_file, test.target()) :
    170222                                        retcode = 1
     
    178230
    179231                else:
    180                         with open (out_file, "r") as myfile:
    181                                 error = myfile.read()
     232                        if os.stat(out_file).st_size < 1048576:
     233                                with open (out_file, "r", encoding='latin-1') as myfile:  # use latin-1 so all chars mean something.
     234                                        error = myfile.read()
     235                        else:
     236                                error = "Output log can't be read, file is bigger than 1MB, see {} for actual error\n".format(out_file)
    182237
    183238                        ret, info = core_info(exe_file)
     
    214269        except KeyboardInterrupt:
    215270                return False, ""
    216         except Exception as ex:
    217                 print("Unexpected error in worker thread: %s" % ex, file=sys.stderr)
    218                 sys.stderr.flush()
    219                 return False, ""
     271        # except Exception as ex:
     272        #       print("Unexpected error in worker thread running {}: {}".format(t.target(), ex), file=sys.stderr)
     273        #       sys.stderr.flush()
     274        #       return False, ""
    220275
    221276
     
    225280        make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL)
    226281
    227         # since python prints stacks by default on a interrupt, redo the interrupt handling to be silent
    228         def worker_init():
    229                 def sig_int(signal_num, frame):
    230                         pass
    231 
    232                 signal.signal(signal.SIGINT, sig_int)
    233 
    234         # create the executor for our jobs and handle the signal properly
    235         pool = multiprocessing.Pool(jobs, worker_init)
     282        # create the executor for our jobs
     283        pool = multiprocessing.Pool(jobs)
    236284
    237285        failed = False
    238 
    239         def stop(x, y):
    240                 print("Tests interrupted by user", file=sys.stderr)
    241                 sys.exit(1)
    242         signal.signal(signal.SIGINT, stop)
    243286
    244287        # for each test to run
     
    278321        make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL)
    279322
    280         return 1 if failed else 0
     323        return failed
    281324
    282325
     
    292335        settings.init( options )
    293336
     337        # --------------------------------------------------
     338        # list all the test for auto completion programs
     339        # not pretty, single line, with the command line options
     340        if options.list_comp :
     341                # fetch the liest of all valid tests
     342                tests = list_tests( None, None )
     343
     344                # print the possible options
     345                print("-h --help --debug --dry-run --list --ast=new --ast=old --arch --all --regenerate-expected --archive-errors --install --timeout --global-timeout --timeout-with-gdb -j --jobs -I --include -E --exclude --continue ", end='')
     346                print(" ".join(map(lambda t: "%s" % (t.target()), tests)))
     347
     348                # done
     349                sys.exit(0)
     350
     351        # --------------------------------------------------
     352        # list all the test for auto completion programs
     353        if options.list_dist :
     354                # fetch the liest of all valid tests
     355                tests = list_tests( None, None )
     356
     357                for t in tests:
     358                        print(os.path.relpath(t.expect(), settings.SRCDIR), end=' ')
     359                        print(os.path.relpath(t.input() , settings.SRCDIR), end=' ')
     360                        code, out = make_recon(t.target())
     361
     362                        if code != 0:
     363                                print('ERROR: recond failed for test {}'.format(t.target()), file=sys.stderr)
     364                                sys.exit(1)
     365
     366                        print(' '.join(re.findall('([^\s]+\.cfa)', out)), end=' ')
     367
     368                print('')
     369
     370                # done
     371                sys.exit(0)
     372
     373
     374        # --------------------------------------------------
     375        # list all the tests for users, in a pretty format
     376        if options.list :
     377                # fetch the liest of all valid tests
     378                tests = list_tests( options.include, options.exclude )
     379
     380                # print the available tests
     381                fancy_print("\n".join(map(lambda t: t.toString(), tests)))
     382
     383                # done
     384                sys.exit(0)
     385
    294386        # fetch the liest of all valid tests
    295387        all_tests = list_tests( options.include, options.exclude )
    296388
    297 
    298389        # if user wants all tests than no other treatement of the test list is required
    299         if options.all or options.list or options.list_comp or options.include :
     390        if options.all or options.include :
    300391                tests = all_tests
    301392
     
    309400                sys.exit(1)
    310401
    311 
    312         # sort the test alphabetically for convenience
    313         tests.sort(key=lambda t: (t.arch if t.arch else '') + t.target())
    314 
    315         # users may want to simply list the tests
    316         if options.list_comp :
    317                 print("-h --help --debug --dry-run --list --arch --all --regenerate-expected --archive-errors --install --timeout --global-timeout -j --jobs ", end='')
    318                 print(" ".join(map(lambda t: "%s" % (t.target()), tests)))
    319 
    320         elif options.list :
    321                 print("Listing for %s:%s"% (settings.arch.string, settings.debug.string))
    322                 fancy_print("\n".join(map(lambda t: t.toString(), tests)))
    323 
    324         else :
    325                 # check the build configuration works
    326                 settings.prep_output(tests)
    327                 settings.validate()
    328 
    329                 options.jobs, forceJobs = job_count( options, tests )
    330                 settings.update_make_cmd(forceJobs, options.jobs)
    331 
    332                 print('%s %i tests on %i cores (%s:%s)' % (
    333                         'Regenerating' if settings.generating else 'Running',
    334                         len(tests),
    335                         options.jobs,
    336                         settings.arch.string,
    337                         settings.debug.string
    338                 ))
    339 
    340                 # otherwise run all tests and make sure to return the correct error code
    341                 sys.exit( run_tests(tests, options.jobs) )
     402        # prep invariants
     403        settings.prep_output(tests)
     404        failed = 0
     405
     406        # check if the expected files aren't empty
     407        if not options.regenerate_expected:
     408                for t in tests:
     409                        if is_empty(t.expect()):
     410                                print('WARNING: test "{}" has empty .expect file'.format(t.target()), file=sys.stderr)
     411
     412        # for each build configurations, run the test
     413        with Timed() as total_dur:
     414                for ast, arch, debug, install in itertools.product(settings.all_ast, settings.all_arch, settings.all_debug, settings.all_install):
     415                        settings.ast     = ast
     416                        settings.arch    = arch
     417                        settings.debug   = debug
     418                        settings.install = install
     419
     420                        # filter out the tests for a different architecture
     421                        # tests are the same across debug/install
     422                        local_tests = settings.ast.filter( tests )
     423                        local_tests = settings.arch.filter( local_tests )
     424                        options.jobs, forceJobs = job_count( options, local_tests )
     425                        settings.update_make_cmd(forceJobs, options.jobs)
     426
     427                        # check the build configuration works
     428                        settings.validate()
     429
     430                        # print configuration
     431                        print('%s %i tests on %i cores (%s:%s - %s)' % (
     432                                'Regenerating' if settings.generating else 'Running',
     433                                len(local_tests),
     434                                options.jobs,
     435                                settings.ast.string,
     436                                settings.arch.string,
     437                                settings.debug.string
     438                        ))
     439                        if not local_tests :
     440                                print('WARNING: No tests for this configuration')
     441                                continue
     442
     443                        # otherwise run all tests and make sure to return the correct error code
     444                        failed = run_tests(local_tests, options.jobs)
     445                        if failed:
     446                                result = 1
     447                                if not settings.continue_:
     448                                        break
     449
     450        print('Tests took %s' % fmtDur( total_dur.duration ))
     451        sys.exit( failed )
  • tests/time.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Tue Mar 27 17:24:56 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan  5 18:27:37 2020
    13 // Update Count     : 34
     12// Last Modified On : Thu Jun 18 18:14:49 2020
     13// Update Count     : 37
    1414//
    1515
     
    2020        Duration d1 = 3`h, d2 = 2`s, d3 = 3.375`s, d4 = 12`s, d5 = 1`s + 10_000`ns;
    2121        sout | d1 | d2 | d3 | d4 | d5;
     22        sout | d1`dd | d2`dm | d3`ds | d4`dms | d5`dus;
    2223        d1 = 0;
    2324        sout | d1 | d2 | d3;
  • tests/tuple/tupleAssign.cfa

    r3c64c668 r58fe85a  
    4444                double d = 0.0;
    4545                int i = 0;
    46                 char c = '\0';
     46                signed char c = '\0';
    4747                struct X {
    4848                        int z;
     
    5555                [t, x, d, i, c, x] = (double)94.12;
    5656                printf( "d=%lg i=%d c=%c t=[%d, %lg, %d]\n", d, i, (int)c, t );
    57                 sout | "d=" | d | "i=" | i | "c=" | c | ' ' | "t=[" | t | "]";
     57                sout | "d=" | d | "i=" | i | "c=" | (char)c | ' ' | "t=[" | t | "]";
    5858                [x, c, i, d, x, t] = (double)-94.12;
    5959                printf( "d=%lg i=%d c=%c t=[%d, %lg, %d]\n", d, i, c, t );
    60                 sout | "d=" | d | "i=" | i | "c=" | c | ' ' | "t=[" | t | "]";
     60                sout | "d=" | d | "i=" | i | "c=" | (char)c | ' ' | "t=[" | t | "]";
    6161        }
    6262}
  • tests/typedefRedef.cfa

    r3c64c668 r58fe85a  
    2727typedef int ARR[];
    2828typedef int ARR[];
    29 // #ifdef ERR1
    30 // if a typedef has an array dimension,
    31 // it can only be redefined to the same dimension
     29#ifdef ERR1
     30// if a typedef has an array dimension, it can only be redefined to the same dimension
    3231typedef int ARR[2];
    33 // #endif
     32#endif
    3433
    3534typedef int X;
     
    5453
    5554int main() {
    56   typedef int ARR[sz];
     55        typedef int ARR[sz];
    5756
    58   // can't redefine typedef which is VLA
     57        // can't redefine typedef which is VLA
    5958#if ERR1
    60   typedef int ARR[sz];
     59        typedef int ARR[sz];
    6160#endif
    6261
    63   Foo *x;
     62        Foo * x;
    6463
    65   typedef struct Bar Foo;
    66   Foo *y;
     64        typedef struct Bar Foo;
     65        Foo * y;
    6766
    68   typedef int *** pt;
     67        typedef int *** pt;
     68
     69        #pragma message( "Compiled" )                   // force non-empty .expect file
    6970}
  • tests/typeof.cfa

    r3c64c668 r58fe85a  
    11int main() {
    2     int *v1;
    3     typeof(v1) v2;
    4     typeof(*v1) v3[4];
    5     char *v4[4];
    6     typeof(typeof(char *)[4]) v5;
    7     typeof (int *) v6;
    8     typeof( int ( int, int p ) ) *v7;
    9     typeof( [int] ( int, int p ) ) *v8;
    10     (typeof(v1)) v2; // cast with typeof
     2        int *v1;
     3        typeof(v1) v2;
     4        typeof(*v1) v3[4];
     5        char *v4[4];
     6        typeof(typeof(char *)[4]) v5;
     7        typeof (int *) v6;
     8        typeof( int ( int, int p ) ) *v7;
     9        typeof( [int] ( int, int p ) ) *v8;
     10        (typeof(v1)) v2; // cast with typeof
     11        printf( "done\n" );                             // non-empty .expect file
    1112}
  • tests/variableDeclarator.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Wed Aug 17 08:41:42 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Nov  6 18:02:16 2018
    13 // Update Count     : 2
     12// Last Modified On : Sun Sep 27 07:46:17 2020
     13// Update Count     : 13
    1414//
    1515
     
    1818int (f2);
    1919
    20 int *f3;
    21 int **f4;
    22 int * const *f5;
     20int * f3;
     21int ** f4;
     22int * const * f5;
    2323int * const * const f6;
    2424
    25 int *(f7);
    26 int **(f8);
    27 int * const *(f9);
     25int * (f7);
     26int ** (f8);
     27int * const * (f9);
    2828int * const * const (f10);
    2929
    30 int (*f11);
    31 int (**f12);
    32 int (* const *f13);
     30int (* f11);
     31int (** f12);
     32int (* const * f13);
    3333int (* const * const f14);
    3434
    35 int f15[];
     35int f15[0];
    3636int f16[10];
    37 int (f17[]);
     37int (f17[0]);
    3838int (f18[10]);
    3939
    40 int *f19[];
    41 int *f20[10];
    42 int **f21[];
    43 int **f22[10];
    44 int * const *f23[];
    45 int * const *f24[10];
    46 int * const * const f25[];
     40int * f19[0];
     41int * f20[10];
     42int ** f21[0];
     43int ** f22[10];
     44int * const * f23[0];
     45int * const * f24[10];
     46int * const * const f25[0];
    4747int * const * const f26[10];
    4848
    49 int *(f27[]);
     49int *(f27[0]);
    5050int *(f28[10]);
    51 int **(f29[]);
     51int **(f29[0]);
    5252int **(f30[10]);
    53 int * const *(f31[]);
     53int * const *(f31[0]);
    5454int * const *(f32[10]);
    55 int * const * const (f33[]);
     55int * const * const (f33[0]);
    5656int * const * const (f34[10]);
    5757
    58 int (*f35)[];
    59 int (*f36)[10];
    60 int (**f37)[];
    61 int (**f38)[10];
    62 int (* const *f39)[];
    63 int (* const *f40)[10];
     58int (* f35)[];
     59int (* f36)[10];
     60int (** f37)[];
     61int (** f38)[10];
     62int (* const * f39)[];
     63int (* const * f40)[10];
    6464int (* const * const f41)[];
    6565int (* const * const f42)[10];
    6666
    67 int f43[][3];
     67int f43[0][3];
    6868int f44[3][3];
    69 int (f45[])[3];
     69int (f45[0])[3];
    7070int (f46[3])[3];
    71 int ((f47[]))[3];
     71int ((f47[0]))[3];
    7272int ((f48[3]))[3];
    7373
    74 int *f49[][3];
    75 int *f50[3][3];
    76 int **f51[][3];
    77 int **f52[3][3];
    78 int * const *f53[][3];
    79 int * const *f54[3][3];
    80 int * const * const f55[][3];
     74int * f49[0][3];
     75int * f50[3][3];
     76int ** f51[0][3];
     77int ** f52[3][3];
     78int * const * f53[0][3];
     79int * const * f54[3][3];
     80int * const * const f55[0][3];
    8181int * const * const f56[3][3];
    8282
    83 int (*f57[][3]);
    84 int (*f58[3][3]);
    85 int (**f59[][3]);
    86 int (**f60[3][3]);
    87 int (* const *f61[][3]);
    88 int (* const *f62[3][3]);
    89 int (* const * const f63[][3]);
     83int (* f57[0][3]);
     84int (* f58[3][3]);
     85int (** f59[0][3]);
     86int (** f60[3][3]);
     87int (* const * f61[0][3]);
     88int (* const * f62[3][3]);
     89int (* const * const f63[0][3]);
    9090int (* const * const f64[3][3]);
    9191
     
    9393int (f66)(int);
    9494
    95 int *f67(int);
    96 int **f68(int);
    97 int * const *f69(int);
     95int * f67(int);
     96int ** f68(int);
     97int * const * f69(int);
    9898int * const * const f70(int);
    9999
     
    104104int * const * const (f74)(int);
    105105
    106 int (*f75)(int);
    107 int (**f76)(int);
    108 int (* const *f77)(int);
     106int (* f75)(int);
     107int (** f76)(int);
     108int (* const * f77)(int);
    109109int (* const * const f78)(int);
    110110
    111 int (*(*f79)(int))();
     111int (*(* f79)(int))();
    112112int (*(* const f80)(int))();
    113113int (* const(* const f81)(int))();
     
    119119//int fe2()[];                          // returning an array
    120120//int fe3()();                          // returning a function
    121 //int (*fe4)()();                               // returning a function
    122 //int ((*fe5())())[];                   // returning an array
     121//int (* fe4)()();                              // returning a function
     122//int ((* fe5())())[];                  // returning an array
    123123
     124#ifdef __CFA__
    124125// Cforall extensions
    125126
     
    129130const * const * int cf6;
    130131
    131 [] int cf15;
     132[0] int cf15;
    132133[10] int cf16;
    133134
    134 [] * int cf19;
     135[0] * int cf19;
    135136[10] * int cf20;
    136 int **cf21[];
     137int ** cf21[0];
    137138[10] * * int cf22;
    138 [] * const * int cf23;
     139[0] * const * int cf23;
    139140[10] * const * int cf24;
    140 [] const * const * int cf25;
     141[0] const * const * int cf25;
    141142[10] const * const * int cf26;
    142143
     
    150151const * const * [10] int cf42;
    151152
    152 [][3] int cf43;
     153[0][3] int cf43;
    153154[3][3] int cf44;
    154155
    155 [][3] * int cf49;
     156[0][3] * int cf49;
    156157[3][3] * int cf50;
    157 [][3] * * int cf51;
     158[0][3] * * int cf51;
    158159[3][3] * * int cf52;
    159 [][3] const * int cf53;
     160[0][3] const * int cf53;
    160161[3][3] * const * int cf54;
    161 [][3] const * const * int cf55;
     162[0][3] const * const * int cf55;
    162163[3][3] const * const * int cf56;
    163164
     
    173174
    174175*[]*[]* [ *[]*[] int ]( *[]*[] int, *[]*[] int ) v3;
     176#endif // __CFA__
    175177
    176178//Dummy main
    177 int main(int argc, char const *argv[])
    178 {
    179         return 0;
     179int main( int argc, char const * argv[] ) {
     180        #pragma message( "Compiled" )                                           // force non-empty .expect file
    180181}
    181182
  • tests/vector.cfa

    r3c64c668 r58fe85a  
    1414//
    1515
     16#include <vector.hfa>
    1617#include <fstream.hfa>
    17 #include <vector.hfa>
    1818
    1919#undef assert
     
    2828int main() {
    2929        vector( int ) iv;
     30
     31        assert( ((uintptr_t)&iv.storage.storage ) == (((uintptr_t)&iv)) );
     32        assert( ((uintptr_t)&iv.storage.capacity) == (((uintptr_t)&iv) + sizeof(void *)) );
     33        assert( ((uintptr_t)&iv.size            ) == (((uintptr_t)&iv) + sizeof(void *) + sizeof(size_t)) );
    3034
    3135        assert( empty( &iv ) );
  • tests/voidPtr.cfa

    r3c64c668 r58fe85a  
    1313        if ( ! a ) {
    1414                abort();
    15         }       
     15        }
     16        printf( "done\n" );                             // non-empty .expect file
    1617}
    1718
  • tests/warnings/self-assignment.cfa

    r3c64c668 r58fe85a  
    1010// Created On       : Thu Mar 1 13:53:57 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Feb 20 07:56:17 2019
    13 // Update Count     : 3
     12// Last Modified On : Sun Sep 27 09:24:34 2020
     13// Update Count     : 6
    1414//
    1515
     
    3131        s.i = s.i;
    3232        t.s.i = t.s.i;
     33
     34        #pragma message( "Compiled" )                   // force non-empty .expect file
    3335}
    3436
    3537// Local Variables: //
    3638// tab-width: 4 //
    37 // compile-command: "cfa dtor-early-exit" //
     39// compile-command: "cfa self-assignment.cfa" //
    3840// End: //
  • tests/zombies/structMember.cfa

    r3c64c668 r58fe85a  
    5353// C useless declarations
    5454
     55#ifdef ERROR
    5556        int;
    5657        TD;
     
    7071        W(int);
    7172        W(int).X;
     73#endif // ERROR
    7274};
    7375
  • tools/Makefile.am

    r3c64c668 r58fe85a  
    1818ACLOCAL_AMFLAGS  = -I automake
    1919
     20noinst_PROGRAMS = busy catchsig repeat watchdog
    2021AM_CFLAGS = -Wall -Wextra -O2 -g
     22busy_LDFLAGS     = -pthread
    2123
    22 noinst_PROGRAMS = busy catchsig repeat watchdog
    23 
    24 busy_SOURCES     = busy.c
    25 busy_LDFLAGS     = -pthread
    26 catchsig_SOURCES = catchsig.c
    27 repeat_SOURCES   = repeat.c
    28 watchdog_SOURCES = watchdog.c
     24nodist_busy_SOURCES     = busy.c
     25nodist_catchsig_SOURCES = catchsig.c
     26nodist_repeat_SOURCES   = repeat.c
     27nodist_watchdog_SOURCES = watchdog.c
  • tools/build/push2dist.sh

    r3c64c668 r58fe85a  
    22
    33hash="$1"
     4bwlim="$2"
    45valid=$(distcc -j 2> /dev/null)
    56# if test "${valid}" != 0
     
    1920# echo "Copying to machines : ${hosts} (hash=${hash})"
    2021
    21 files="../../../driver/cfa ../../../driver/cfa-cpp ../../../driver/cc1 ../../../driver/as $(find . -name '*.c*' | tr '\n' ' ')"
     22files="../../../driver/cfa ../../../driver/cfa-cpp ../../../driver/cc1 ../../../driver/as defines.hfa $(find . -name '*.c*' | tr '\n' ' ')"
    2223# echo "Files ${files}"
    2324
    2425function push() {
    2526        ssh ${host} "mkdir -p ~/.cfadistcc/${hash}/"
    26         rsync -a ${dV} ${files} ${host}:~/.cfadistcc/${hash}/.
     27        rsync --bwlimit=${bwlim} -a ${dV} ${files} ${host}:~/.cfadistcc/${hash}/.
    2728}
    2829
  • tools/cfa.nanorc

    r3c64c668 r58fe85a  
    1414
    1515# Declarations
    16 color brightgreen "\<(struct|union|typedef|trait|coroutine|monitor|thread)\>"
    17 color brightgreen "\<(with)\>"
     16color brightgreen "\<(struct|union|typedef|trait|coroutine|generator)\>"
     17color brightgreen "\<(monitor|thread|with)\>"
    1818
    1919# Control Flow Structures
    2020color brightyellow "\<(if|else|while|do|for|switch|choose|case|default)\>"
    21 color brightyellow "\<(disable|enable|waitfor|when|timeout)\>"
     21color brightyellow "\<(disable|enable|waitfor|when|timeout|suspend)\>"
    2222color brightyellow "\<(try|catch(Resume)?|finally)\>"
    2323
     
    2626
    2727# Escaped Keywords, now Identifiers.
    28 color white "`\w+`"
     28color white "``\w+"
    2929
    3030# Operator Names
     
    3737## Update/Redistribute
    3838# GCC builtins
    39 color cyan "__attribute__[[:space:]]*\(\([^()]*(\([^()]*\)[^()]*)*\)\)"
     39color cyan "__attribute__[[:space:]]*\(\(([^)]|[^)]\))*\)\)"
    4040##color cyan "__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__"
    4141
  • tools/prettyprinter/Makefile.am

    r3c64c668 r58fe85a  
    3030tools_prettyprinter_PROGRAMS = pretty
    3131tools_prettyprinterdir = ../
    32 pretty_SOURCES = ${SRC}
     32nodist_pretty_SOURCES = ${SRC}
    3333pretty_LDADD = ${LEXLIB} -ldl                   # yywrap
    3434pretty_CXXFLAGS = -Wno-deprecated -Wall -DYY_NO_INPUT -O2 -g -std=c++14
  • tools/stat.py

    r3c64c668 r58fe85a  
    1 #!/usr/bin/python
     1#!/usr/bin/python3
    22
    33import sys
     
    1717                avg = numpy.mean  (content)
    1818                std = numpy.std   (content)
    19                 print "median {0:.1f} avg {1:.1f} stddev {2:.1f}".format( med, avg, std )
     19                print("median {0:.1f} avg {1:.1f} stddev {2:.1f}".format( med, avg, std ))
    2020
    2121
  • tools/vscode/uwaterloo.cforall-0.1.0/package.json

    r3c64c668 r58fe85a  
    22        "name": "cforall",
    33        "version": "0.1.0",
    4         "displayName": "Cforall Language Support",
     4        "displayName": "Cāˆ€ (C-for-all) Language Support",
    55        "description": "Cforall - colorizer, grammar and snippets.",
    66        "publisher": "uwaterloo",
     
    99                "vscode": "^1.5.0"
    1010        },
    11         "icon": "images/icon.svg",
     11        "icon": "images/icon.png",
    1212        "categories": [
    13                 "Languages",
     13                "Programming Languages",
    1414                "Linters",
    1515                "Other"
    1616        ],
     17        "activationEvents": [
     18                "onLanguage:cforall"
     19        ],
     20        "main": "./client/main.js",
    1721        "contributes": {
    1822                "languages": [
     
    2125                                "aliases": [
    2226                                        "Cāˆ€",
     27                                        "CForAll",
    2328                                        "Cforall",
    24                                         "CForAll",
    2529                                        "cforall"
    2630                                ],
    2731                                "extensions": [
    28                                         ".cf"
     32                                        ".cfa",
     33                                        ".hfa",
     34                                        ".ifa"
    2935                                ],
    3036                                "configuration": "./cforall.configuration.json"
     
    3440                        {
    3541                                "language": "cforall",
    36                                 "scopeName": "source.cf",
    37                                 "path": "./syntaxes/cfa.tmLanguage"
     42                                "scopeName": "source.cfa",
     43                                "path": "./syntaxes/cfa.tmLanguage.json"
    3844                        }
    39                 ]
     45                ],
     46                "configuration": {
     47                        "type": "object",
     48                        "title": "Example configuration",
     49                        "properties": {
     50                                "cforall.maxNumberOfProblems": {
     51                                        "scope": "resource",
     52                                        "type": "number",
     53                                        "default": 100,
     54                                        "description": "Controls the maximum number of problems produced by the server."
     55                                },
     56                                "cforall.trace.server": {
     57                                        "scope": "window",
     58                                        "type": "string",
     59                                        "enum": [
     60                                                "off",
     61                                                "messages",
     62                                                "verbose"
     63                                        ],
     64                                        "default": "off",
     65                                        "description": "Traces the communication between VS Code and the language server."
     66                                }
     67                        }
     68                }
     69        },
     70        "dependencies": {
     71                "vscode-languageclient": "^4.1.4"
     72        },
     73        "devDependencies": {
     74                "vscode-languageclient": "^4.1.4"
    4075        }
    4176}
Note: See TracChangeset for help on using the changeset viewer.