Changeset d95969a
- Timestamp:
- Jan 25, 2021, 3:45:42 PM (5 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- c292244
- Parents:
- b6a8b31 (diff), 7158202 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)links above to see all the changes relative to each parent. - Files:
-
- 14 added
- 173 edited
- 1 moved
-
.gitignore (modified) (1 diff)
-
benchmark/io/http/filecache.cfa (modified) (4 diffs)
-
benchmark/io/http/http_ring.cpp (added)
-
benchmark/io/http/main.cfa (modified) (7 diffs)
-
benchmark/io/http/options.cfa (modified) (8 diffs)
-
benchmark/io/http/options.hfa (modified) (2 diffs)
-
benchmark/io/http/protocol.cfa (modified) (11 diffs)
-
benchmark/io/http/protocol.hfa (modified) (2 diffs)
-
benchmark/io/http/worker.cfa (modified) (6 diffs)
-
benchmark/io/setup.sh (added)
-
doc/LaTeXmacros/common.tex (modified) (4 diffs)
-
doc/bibliography/pl.bib (modified) (4 diffs)
-
doc/theses/andrew_beach_MMath/.gitignore (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/Makefile (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/existing.tex (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/features.tex (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/future.tex (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/implement.tex (modified) (21 diffs)
-
doc/theses/andrew_beach_MMath/unwinding.tex (modified) (11 diffs)
-
doc/theses/andrew_beach_MMath/uw-ethesis-frontpgs.tex (added)
-
doc/theses/andrew_beach_MMath/uw-ethesis.bib (added)
-
doc/theses/andrew_beach_MMath/uw-ethesis.tex (added)
-
doc/theses/fangren_yu_COOP_F20/Report.tex (modified) (18 diffs)
-
doc/theses/fangren_yu_COOP_S20/Report.tex (modified) (1 diff)
-
driver/cfa.cc (modified) (2 diffs)
-
example/io/attach-block.c (added)
-
example/io/attach-end.c (added)
-
example/io/batch-readv.c (moved) (moved from benchmark/io/batch-readv.c )
-
libcfa/prelude/builtins.c (modified) (7 diffs)
-
libcfa/prelude/prelude-gen.cc (modified) (6 diffs)
-
libcfa/prelude/prelude.old.cf (modified) (11 diffs)
-
libcfa/prelude/sync-builtins.cf (modified) (6 diffs)
-
libcfa/src/Makefile.am (modified) (1 diff)
-
libcfa/src/bitmanip.hfa (modified) (2 diffs)
-
libcfa/src/bits/algorithm.hfa (modified) (7 diffs)
-
libcfa/src/bits/collection.hfa (modified) (5 diffs)
-
libcfa/src/bits/containers.hfa (modified) (10 diffs)
-
libcfa/src/bits/defs.hfa (modified) (2 diffs)
-
libcfa/src/bits/locks.hfa (modified) (3 diffs)
-
libcfa/src/bits/queue.hfa (modified) (2 diffs)
-
libcfa/src/bits/sequence.hfa (modified) (9 diffs)
-
libcfa/src/bits/stack.hfa (modified) (2 diffs)
-
libcfa/src/bits/weakso_locks.cfa (added)
-
libcfa/src/bits/weakso_locks.hfa (added)
-
libcfa/src/common.cfa (modified) (1 diff)
-
libcfa/src/common.hfa (modified) (4 diffs)
-
libcfa/src/concurrency/coroutine.cfa (modified) (4 diffs)
-
libcfa/src/concurrency/coroutine.hfa (modified) (5 diffs)
-
libcfa/src/concurrency/future.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/io.cfa (modified) (8 diffs)
-
libcfa/src/concurrency/io/setup.cfa (modified) (8 diffs)
-
libcfa/src/concurrency/io/types.hfa (modified) (3 diffs)
-
libcfa/src/concurrency/kernel.cfa (modified) (6 diffs)
-
libcfa/src/concurrency/kernel.hfa (modified) (3 diffs)
-
libcfa/src/concurrency/kernel/fwd.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/kernel/startup.cfa (modified) (3 diffs)
-
libcfa/src/concurrency/locks.cfa (modified) (6 diffs)
-
libcfa/src/concurrency/locks.hfa (modified) (4 diffs)
-
libcfa/src/concurrency/monitor.cfa (modified) (2 diffs)
-
libcfa/src/concurrency/monitor.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/mutex.cfa (modified) (3 diffs)
-
libcfa/src/concurrency/mutex.hfa (modified) (3 diffs)
-
libcfa/src/concurrency/preemption.cfa (modified) (1 diff)
-
libcfa/src/concurrency/stats.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/thread.cfa (modified) (8 diffs)
-
libcfa/src/concurrency/thread.hfa (modified) (8 diffs)
-
libcfa/src/containers/list.hfa (modified) (4 diffs)
-
libcfa/src/containers/maybe.cfa (modified) (7 diffs)
-
libcfa/src/containers/maybe.hfa (modified) (2 diffs)
-
libcfa/src/containers/pair.cfa (modified) (4 diffs)
-
libcfa/src/containers/pair.hfa (modified) (1 diff)
-
libcfa/src/containers/result.cfa (modified) (10 diffs)
-
libcfa/src/containers/result.hfa (modified) (3 diffs)
-
libcfa/src/containers/stackLockFree.hfa (modified) (5 diffs)
-
libcfa/src/containers/vector.cfa (modified) (13 diffs)
-
libcfa/src/containers/vector.hfa (modified) (15 diffs)
-
libcfa/src/exception.h (modified) (4 diffs)
-
libcfa/src/executor.cfa (modified) (1 diff)
-
libcfa/src/gmp.hfa (modified) (2 diffs)
-
libcfa/src/interpose.cfa (modified) (4 diffs)
-
libcfa/src/iostream.cfa (modified) (18 diffs)
-
libcfa/src/iostream.hfa (modified) (16 diffs)
-
libcfa/src/iterator.cfa (modified) (2 diffs)
-
libcfa/src/iterator.hfa (modified) (3 diffs)
-
libcfa/src/math.hfa (modified) (4 diffs)
-
libcfa/src/memory.cfa (modified) (11 diffs)
-
libcfa/src/memory.hfa (modified) (6 diffs)
-
libcfa/src/parseargs.cfa (modified) (7 diffs)
-
libcfa/src/parseargs.hfa (modified) (3 diffs)
-
libcfa/src/rational.cfa (modified) (5 diffs)
-
libcfa/src/rational.hfa (modified) (4 diffs)
-
libcfa/src/stdlib.cfa (modified) (5 diffs)
-
libcfa/src/stdlib.hfa (modified) (16 diffs)
-
libcfa/src/time.cfa (modified) (2 diffs)
-
libcfa/src/vec/vec.hfa (modified) (7 diffs)
-
libcfa/src/vec/vec2.hfa (modified) (3 diffs)
-
libcfa/src/vec/vec3.hfa (modified) (3 diffs)
-
libcfa/src/vec/vec4.hfa (modified) (3 diffs)
-
src/Parser/parser.yy (modified) (1 diff)
-
src/ResolvExpr/PolyCost.cc (modified) (2 diffs)
-
src/ResolvExpr/SpecCost.cc (modified) (6 diffs)
-
tests/.expect/poly-selection.txt (added)
-
tests/alloc2.cfa (modified) (1 diff)
-
tests/avltree/avl-private.cfa (modified) (8 diffs)
-
tests/avltree/avl-private.h (modified) (1 diff)
-
tests/avltree/avl.h (modified) (3 diffs)
-
tests/avltree/avl0.cfa (modified) (1 diff)
-
tests/avltree/avl1.cfa (modified) (3 diffs)
-
tests/avltree/avl2.cfa (modified) (3 diffs)
-
tests/avltree/avl3.cfa (modified) (6 diffs)
-
tests/avltree/avl4.cfa (modified) (2 diffs)
-
tests/bugs/10.cfa (modified) (1 diff)
-
tests/bugs/104.cfa (modified) (1 diff)
-
tests/bugs/194.cfa (modified) (1 diff)
-
tests/bugs/196.cfa (modified) (2 diffs)
-
tests/bugs/203-2.cfa (modified) (1 diff)
-
tests/bugs/203-7.cfa (modified) (1 diff)
-
tests/bugs/203-9.cfa (modified) (1 diff)
-
tests/bugs/233.cfa (added)
-
tests/bugs/7.cfa (modified) (2 diffs)
-
tests/castError.cfa (modified) (1 diff)
-
tests/concurrent/examples/boundedBufferEXT.cfa (modified) (1 diff)
-
tests/concurrent/examples/boundedBufferINT.cfa (modified) (1 diff)
-
tests/concurrent/examples/quickSort.generic.cfa (modified) (1 diff)
-
tests/concurrent/multi-monitor.cfa (modified) (1 diff)
-
tests/concurrent/thread.cfa (modified) (1 diff)
-
tests/errors/completeType.cfa (modified) (4 diffs)
-
tests/exceptions/defaults.cfa (modified) (1 diff)
-
tests/exceptions/polymorphic.cfa (modified) (2 diffs)
-
tests/exceptions/virtual-poly.cfa (modified) (2 diffs)
-
tests/forall.cfa (modified) (11 diffs)
-
tests/function-operator.cfa (modified) (7 diffs)
-
tests/genericUnion.cfa (modified) (3 diffs)
-
tests/global-monomorph.cfa (modified) (1 diff)
-
tests/identity.cfa (modified) (1 diff)
-
tests/init1.cfa (modified) (1 diff)
-
tests/linking/.expect/weakso_nothd.txt (added)
-
tests/linking/weakso_nothd.cfa (added)
-
tests/nested-types.cfa (modified) (1 diff)
-
tests/poly-d-cycle.cfa (modified) (1 diff)
-
tests/poly-o-cycle.cfa (modified) (1 diff)
-
tests/poly-selection.cfa (added)
-
tests/polymorphism.cfa (modified) (4 diffs)
-
tests/raii/ctor-autogen.cfa (modified) (4 diffs)
-
tests/simpleGenericTriple.cfa (modified) (1 diff)
-
tests/sum.cfa (modified) (4 diffs)
-
tests/tuple/tuplePolymorphism.cfa (modified) (2 diffs)
-
tests/tuple/tupleVariadic.cfa (modified) (3 diffs)
-
tests/zombies/ArrayN.c (modified) (1 diff)
-
tests/zombies/Members.c (modified) (1 diff)
-
tests/zombies/Rank2.c (modified) (2 diffs)
-
tests/zombies/abstype.c (modified) (1 diff)
-
tests/zombies/context.cfa (modified) (1 diff)
-
tests/zombies/gc_no_raii/bug-repro/blockers/explicit_cast.c (modified) (2 diffs)
-
tests/zombies/gc_no_raii/bug-repro/blockers/recursive_realloc.c (modified) (2 diffs)
-
tests/zombies/gc_no_raii/bug-repro/deref.c (modified) (2 diffs)
-
tests/zombies/gc_no_raii/bug-repro/field.c (modified) (2 diffs)
-
tests/zombies/gc_no_raii/bug-repro/malloc.c (modified) (4 diffs)
-
tests/zombies/gc_no_raii/bug-repro/oddtype.c (modified) (1 diff)
-
tests/zombies/gc_no_raii/bug-repro/push_back.h (modified) (8 diffs)
-
tests/zombies/gc_no_raii/bug-repro/realloc.c (modified) (2 diffs)
-
tests/zombies/gc_no_raii/bug-repro/return.c (modified) (3 diffs)
-
tests/zombies/gc_no_raii/bug-repro/return_template.c (modified) (2 diffs)
-
tests/zombies/gc_no_raii/bug-repro/slow_malloc.c (modified) (1 diff)
-
tests/zombies/gc_no_raii/bug-repro/zero.c (modified) (3 diffs)
-
tests/zombies/gc_no_raii/src/gc.h (modified) (1 diff)
-
tests/zombies/gc_no_raii/src/gcpointers.c (modified) (2 diffs)
-
tests/zombies/gc_no_raii/src/gcpointers.h (modified) (3 diffs)
-
tests/zombies/gc_no_raii/src/tools.h (modified) (2 diffs)
-
tests/zombies/hashtable.cfa (modified) (5 diffs)
-
tests/zombies/hashtable2.cfa (modified) (8 diffs)
-
tests/zombies/huge.c (modified) (1 diff)
-
tests/zombies/it_out.c (modified) (3 diffs)
-
tests/zombies/new.c (modified) (1 diff)
-
tests/zombies/occursError.cfa (modified) (1 diff)
-
tests/zombies/prolog.c (modified) (1 diff)
-
tests/zombies/quad.c (modified) (1 diff)
-
tests/zombies/scope.cfa (modified) (1 diff)
-
tests/zombies/simplePoly.c (modified) (1 diff)
-
tests/zombies/simpler.c (modified) (1 diff)
-
tests/zombies/specialize.c (modified) (1 diff)
-
tests/zombies/square.c (modified) (1 diff)
-
tests/zombies/structMember.cfa (modified) (1 diff)
-
tests/zombies/subrange.cfa (modified) (5 diffs)
-
tests/zombies/twice.c (modified) (1 diff)
-
tests/zombies/typeGenerator.cfa (modified) (2 diffs)
-
tests/zombies/withStatement.cfa (modified) (2 diffs)
-
tests/zombies/wrapper/src/pointer.h (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
rb6a8b31 rd95969a 79 79 # generated by npm 80 80 package-lock.json 81 82 # generated by benchmark 83 benchmark/Cargo.toml -
benchmark/io/http/filecache.cfa
rb6a8b31 rd95969a 4 4 #include <string.h> 5 5 6 #include <fstream.hfa> 6 7 #include <stdlib.hfa> 7 8 … … 182 183 conflicts += put_file( raw[i], fd ); 183 184 } 184 printf("Filled cache from path \"%s\" with %zu files\n", path, fcount);185 sout | "Filled cache from path \"" | path | "\" with" | fcount | "files"; 185 186 if( conflicts > 0 ) { 186 printf("Found %d conflicts (seed: %u)\n", conflicts, options.file_cache.hash_seed);187 sout | "Found" | conflicts | "conflicts (seed: " | options.file_cache.hash_seed | ")"; 187 188 #if defined(REJECT_CONFLICTS) 188 189 abort("Conflicts found in the cache"); … … 191 192 192 193 if(options.file_cache.list) { 193 printf("Listing files and exiting\n");194 sout | "Listing files and exiting"; 194 195 for(i; fcount) { 195 196 int s; char u; 196 197 [s, u] = human_size(raw[i].size); 197 printf("%4d%c - %s\n", s, u, raw[i].file);198 sout | s | u | "-" | raw[i].file; 198 199 free(raw[i].file); 199 200 } … … 208 209 209 210 [int *, int] filefds(int extra) { 211 if(!options.file_cache.path) { 212 int * data = alloc(extra); 213 return [data, 0]; 214 } 215 210 216 if(!file_cache.entries) { 211 217 abort("File cache not filled!\n"); -
benchmark/io/http/main.cfa
rb6a8b31 rd95969a 6 6 #include <unistd.h> 7 7 extern "C" { 8 #include <signal.h> 8 9 #include <sys/socket.h> 9 10 #include <netinet/in.h> 10 11 } 11 12 13 #include <fstream.hfa> 12 14 #include <kernel.hfa> 15 #include <iofwd.hfa> 13 16 #include <stats.hfa> 14 17 #include <time.hfa> … … 50 53 51 54 //============================================================================================= 55 // Stats Printer 56 //=============================================================================================' 57 58 thread StatsPrinter {}; 59 60 void ?{}( StatsPrinter & this ) { 61 ((thread&)this){ "Stats Printer Thread" }; 62 } 63 64 void main(StatsPrinter & this) { 65 LOOP: for() { 66 waitfor( ^?{} : this) { 67 break LOOP; 68 } 69 or else {} 70 71 sleep(10`s); 72 73 print_stats_now( *options.clopts.instance, CFA_STATS_READY_Q | CFA_STATS_IO ); 74 } 75 } 76 77 //============================================================================================= 52 78 // Main 53 79 //=============================================================================================' 54 80 int main( int argc, char * argv[] ) { 81 __sighandler_t s = 1p; 82 signal(SIGPIPE, s); 83 55 84 //=================== 56 85 // Parse args 57 const char * path =parse_options(argc, argv);86 parse_options(argc, argv); 58 87 59 88 //=================== 60 89 // Open Files 61 printf("Filling cache from %s\n", path); 62 fill_cache( path ); 90 if( options.file_cache.path ) { 91 sout | "Filling cache from" | options.file_cache.path; 92 fill_cache( options.file_cache.path ); 93 } 63 94 64 95 //=================== 65 96 // Open Socket 66 printf("%ld : Listening on port %d\n", getpid(), options.socket.port);97 sout | getpid() | ": Listening on port" | options.socket.port; 67 98 int server_fd = socket(AF_INET, SOCK_STREAM, 0); 68 99 if(server_fd < 0) { … … 84 115 if(errno == EADDRINUSE) { 85 116 if(waited == 0) { 86 printf("Waiting for port\n");117 sout | "Waiting for port"; 87 118 } else { 88 printf("\r%d", waited);89 f flush(stdout);119 sout | "\r" | waited | nonl; 120 flush( sout ); 90 121 } 91 122 waited ++; … … 122 153 } 123 154 124 if(options.file_cache. fixed_fds) {155 if(options.file_cache.path && options.file_cache.fixed_fds) { 125 156 register_fixed_files(cl, fds, pipe_off); 126 157 } … … 128 159 { 129 160 ServerProc procs[options.clopts.nprocs]; 161 StatsPrinter printer; 130 162 131 163 init_protocol(); … … 148 180 unpark( workers[i] ); 149 181 } 150 printf("%d workers started on %d processors\n", options.clopts.nworkers, options.clopts.nprocs);182 sout | options.clopts.nworkers | "workers started on" | options.clopts.nprocs | "processors"; 151 183 { 152 184 char buffer[128]; 153 while( !feof(stdin)) {154 fgets(buffer, 128, stdin);185 while(int ret = cfa_read(0, buffer, 128, 0, -1`s, 0p, 0p); ret != 0) { 186 if(ret < 0) abort( "main read error: (%d) %s\n", (int)errno, strerror(errno) ); 155 187 } 156 188 157 printf("Shutting Down\n"); 158 } 159 189 sout | "Shutdown received"; 190 } 191 192 sout | "Notifying connections..." | nonl; flush( sout ); 160 193 for(i; options.clopts.nworkers) { 161 printf("Cancelling %p\n", (void*)workers[i].cancel.target);162 194 workers[i].done = true; 163 195 cancel(workers[i].cancel); 164 196 } 165 166 printf("Shutting down socket\n"); 197 sout | "done"; 198 199 sout | "Shutting down socket..." | nonl; flush( sout ); 167 200 int ret = shutdown( server_fd, SHUT_RD ); 168 if( ret < 0 ) { abort( "shutdown error: (%d) %s\n", (int)errno, strerror(errno) ); } 201 if( ret < 0 ) { 202 abort( "shutdown error: (%d) %s\n", (int)errno, strerror(errno) ); 203 } 204 sout | "done"; 169 205 170 206 //=================== 171 207 // Close Socket 172 printf("Closing Socket\n");208 sout | "Closing Socket..." | nonl; flush( sout ); 173 209 ret = close( server_fd ); 174 210 if(ret < 0) { 175 211 abort( "close socket error: (%d) %s\n", (int)errno, strerror(errno) ); 176 212 } 213 sout | "done"; 214 215 sout | "Stopping connection threads..." | nonl; flush( sout ); 177 216 } 178 printf("Workers Closed\n"); 179 217 sout | "done"; 218 219 sout | "Stopping protocol threads..." | nonl; flush( sout ); 180 220 deinit_protocol(); 181 } 182 221 sout | "done"; 222 223 sout | "Stopping processors..." | nonl; flush( sout ); 224 } 225 sout | "done"; 226 227 sout | "Closing splice fds..." | nonl; flush( sout ); 183 228 for(i; pipe_cnt) { 184 229 ret = close( fds[pipe_off + i] ); … … 188 233 } 189 234 free(fds); 190 191 } 235 sout | "done"; 236 237 sout | "Stopping processors..." | nonl; flush( sout ); 238 } 239 sout | "done"; 192 240 193 241 //=================== 194 242 // Close Files 195 printf("Closing Files\n"); 196 close_cache(); 197 } 243 if( options.file_cache.path ) { 244 sout | "Closing open files..." | nonl; flush( sout ); 245 close_cache(); 246 sout | "done"; 247 } 248 } -
benchmark/io/http/options.cfa
rb6a8b31 rd95969a 9 9 } 10 10 11 #include <bitmanip.hfa> 12 #include <fstream.hfa> 11 13 #include <kernel.hfa> 12 14 #include <parseargs.hfa> 13 15 16 #include <stdlib.h> 14 17 #include <string.h> 15 18 … … 18 21 19 22 { // file_cache 23 0, // path 20 24 0, // open_flags; 21 25 42u, // hash_seed; … … 34 38 1, // nprocs; 35 39 1, // nworkers; 36 0, // flags;40 {}, // params; 37 41 false, // procstats 38 42 false, // viewhalts … … 41 45 }; 42 46 43 const char *parse_options( int argc, char * argv[] ) {47 void parse_options( int argc, char * argv[] ) { 44 48 bool subthrd = false; 45 49 bool eagrsub = false; … … 48 52 bool iokpoll = false; 49 53 unsigned sublen = 16; 54 unsigned nentries = 16; 55 50 56 51 57 static cfa_option opt[] = { 52 {'p', "port", "Port the server will listen on", options.socket.port}, 53 {'c', "cpus", "Number of processors to use", options.clopts.nprocs}, 54 {'L', "log", "Enable logs", options.log, parse_settrue}, 55 {'t', "threads", "Number of worker threads to use", options.clopts.nworkers}, 56 {'b', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog}, 57 {'r', "request_len", "Maximum number of bytes in the http request, requests with more data will be answered with Http Code 414", options.socket.buflen}, 58 {'S', "seed", "seed to use for hashing", options.file_cache.hash_seed }, 59 {'C', "cache-size", "Size of the cache to use, if set to small, will uses closes power of 2", options.file_cache.size }, 60 {'l', "list-files", "List the files in the specified path and exit", options.file_cache.list, parse_settrue }, 61 {'s', "submitthread", "If set, cluster uses polling thread to submit I/O", subthrd, parse_settrue }, 62 {'e', "eagersubmit", "If set, cluster submits I/O eagerly but still aggregates submits", eagrsub, parse_settrue}, 63 {'f', "fixed-fds", "If set, files are open eagerly and pre-registered with the cluster", fixedfd, parse_settrue}, 64 {'k', "kpollsubmit", "If set, cluster uses IORING_SETUP_SQPOLL, implies -f", sqkpoll, parse_settrue }, 65 {'i', "kpollcomplete", "If set, cluster uses IORING_SETUP_IOPOLL", iokpoll, parse_settrue }, 66 {'L', "submitlength", "Max number of submitions that can be submitted together", sublen }, 58 { 'p', "port", "Port the server will listen on", options.socket.port}, 59 { 'c', "cpus", "Number of processors to use", options.clopts.nprocs}, 60 { 't', "threads", "Number of worker threads to use", options.clopts.nworkers}, 61 {'\0', "log", "Enable logs", options.log, parse_settrue}, 62 {'\0', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog}, 63 {'\0', "request_len", "Maximum number of bytes in the http request, requests with more data will be answered with Http Code 414", options.socket.buflen}, 64 {'\0', "seed", "seed to use for hashing", options.file_cache.hash_seed }, 65 {'\0', "cache-size", "Size of the cache to use, if set to small, will uses closes power of 2", options.file_cache.size }, 66 {'\0', "list-files", "List the files in the specified path and exit", options.file_cache.list, parse_settrue }, 67 { 's', "submitthread", "If set, cluster uses polling thread to submit I/O", subthrd, parse_settrue }, 68 { 'e', "eagersubmit", "If set, cluster submits I/O eagerly but still aggregates submits", eagrsub, parse_settrue}, 69 { 'f', "fixed-fds", "If set, files are open eagerly and pre-registered with the cluster", fixedfd, parse_settrue}, 70 { 'k', "kpollsubmit", "If set, cluster uses IORING_SETUP_SQPOLL, implies -f", sqkpoll, parse_settrue }, 71 { 'i', "kpollcomplete", "If set, cluster uses IORING_SETUP_IOPOLL", iokpoll, parse_settrue }, 72 {'\0', "submitlength", "Max number of submitions that can be submitted together", sublen }, 73 {'\0', "numentries", "Number of I/O entries", nentries }, 67 74 68 75 }; … … 71 78 char **left; 72 79 parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left ); 80 81 if( !is_pow2(nentries) ) { 82 unsigned v = nentries; 83 v--; 84 v |= v >> 1; 85 v |= v >> 2; 86 v |= v >> 4; 87 v |= v >> 8; 88 v |= v >> 16; 89 v++; 90 serr | "Warning: num_entries not a power of 2" | '(' | nentries | ')' | "raising to " | v; 91 nentries = v; 92 } 93 options.clopts.params.num_entries = nentries; 73 94 74 95 options.clopts.params.poller_submits = subthrd; … … 91 112 options.clopts.params.num_ready = sublen; 92 113 93 if( left[0] == 0p ) { return "."; }114 if( left[0] == 0p ) { return; } 94 115 95 116 const char * path = left[0]; … … 97 118 98 119 if( left[0] != 0p ) { 99 abort("Too many trailing arguments!\n"); 120 serr | "Too many trailing arguments!" | '\'' | path | '\''; 121 while(left[0] != 0p) { 122 serr | " - " | left[0]; 123 left++; 124 } 125 exit(EXIT_FAILURE); 100 126 } 101 127 102 returnpath;128 options.file_cache.path = path; 103 129 } -
benchmark/io/http/options.hfa
rb6a8b31 rd95969a 11 11 12 12 struct { 13 const char * path; 13 14 int open_flags; 14 15 uint32_t hash_seed; … … 36 37 extern Options options; 37 38 38 const char *parse_options( int argc, char * argv[] );39 void parse_options( int argc, char * argv[] ); -
benchmark/io/http/protocol.cfa
rb6a8b31 rd95969a 5 5 #include <fcntl.h> 6 6 } 7 8 #include <fstream.hfa> 7 9 #include <iofwd.hfa> 8 10 … … 11 13 extern "C" { 12 14 int snprintf ( char * s, size_t n, const char * format, ... ); 13 #include <linux/io_uring.h>15 // #include <linux/io_uring.h> 14 16 } 15 17 #include <string.h> … … 24 26 "HTTP/1.1 400 Bad Request\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 25 27 "HTTP/1.1 404 Not Found\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 28 "HTTP/1.1 405 Method Not Allowed\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 29 "HTTP/1.1 408 Request Timeout\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 26 30 "HTTP/1.1 413 Payload Too Large\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 27 31 "HTTP/1.1 414 URI Too Long\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", … … 34 38 400, 35 39 404, 40 405, 41 408, 36 42 413, 37 43 414, … … 49 55 int ret = cfa_write(fd, it, len, 0, -1`s, 0p, 0p); 50 56 // int ret = write(fd, it, len); 51 if( ret < 0 ) { if( errno != EAGAIN && errno != EWOULDBLOCK) abort( "'answer error' error: (%d) %s\n", (int)errno, strerror(errno) ); } 57 if( ret < 0 ) { 58 if( errno == ECONNRESET || errno == EPIPE ) return -ECONNRESET; 59 if( errno == EAGAIN || errno == EWOULDBLOCK) return -EAGAIN; 60 61 abort( "'answer error' error: (%d) %s\n", (int)errno, strerror(errno) ); 62 } 52 63 53 64 // update it/len … … 94 105 if(ret < 0 ) { 95 106 if( errno == EAGAIN || errno == EWOULDBLOCK) continue READ; 96 // if( errno == EINVAL ) return [E400, true, 0, 0]; 107 if( errno == ECONNRESET ) return [E408, true, 0, 0]; 108 if( errno == EPIPE ) return [E408, true, 0, 0]; 97 109 abort( "read error: (%d) %s\n", (int)errno, strerror(errno) ); 98 110 } … … 108 120 } 109 121 110 if( options.log ) printf("%.*s\n", rlen, buffer); 122 if( options.log ) { 123 write(sout, buffer, rlen); 124 sout | nl; 125 } 111 126 112 127 it = buffer; … … 119 134 } 120 135 121 voidsendfile( int pipe[2], int fd, int ans_fd, size_t count ) {136 int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) { 122 137 unsigned sflags = SPLICE_F_MOVE; // | SPLICE_F_MORE; 123 138 off_t offset = 0; … … 128 143 if( ret < 0 ) { 129 144 if( errno != EAGAIN && errno != EWOULDBLOCK) continue SPLICE1; 145 if( errno == ECONNRESET ) return -ECONNRESET; 146 if( errno == EPIPE ) return -EPIPE; 130 147 abort( "splice [0] error: (%d) %s\n", (int)errno, strerror(errno) ); 131 148 } … … 139 156 if( ret < 0 ) { 140 157 if( errno != EAGAIN && errno != EWOULDBLOCK) continue SPLICE2; 158 if( errno == ECONNRESET ) return -ECONNRESET; 159 if( errno == EPIPE ) return -EPIPE; 141 160 abort( "splice [1] error: (%d) %s\n", (int)errno, strerror(errno) ); 142 161 } … … 145 164 146 165 } 166 return count; 147 167 } 148 168 -
benchmark/io/http/protocol.hfa
rb6a8b31 rd95969a 7 7 E400, 8 8 E404, 9 E405, 10 E408, 9 11 E413, 10 12 E414, … … 21 23 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len, io_cancellation *); 22 24 23 voidsendfile( int pipe[2], int fd, int ans_fd, size_t count );25 int sendfile( int pipe[2], int fd, int ans_fd, size_t count ); -
benchmark/io/http/worker.cfa
rb6a8b31 rd95969a 6 6 #include <unistd.h> 7 7 8 #include <fstream.hfa> 8 9 #include <iofwd.hfa> 9 10 … … 33 34 CONNECTION: 34 35 for() { 35 if( options.log ) printf("=== Accepting connection ===\n");36 if( options.log ) sout | "=== Accepting connection ==="; 36 37 int fd = cfa_accept4( this.[sockfd, addr, addrlen, flags], 0, -1`s, &this.cancel, 0p ); 37 38 // int fd = accept4( this.[sockfd, addr, addrlen, flags] ); 38 39 if(fd < 0) { 39 40 if( errno == ECONNABORTED ) break; 40 if( errno == EINVAL && this.done) break;41 if( this.done && (errno == EINVAL || errno == EBADF) ) break; 41 42 abort( "accept error: (%d) %s\n", (int)errno, strerror(errno) ); 42 43 } 43 44 44 if( options.log ) printf("=== New connection %d, waiting for requests ===\n", fd);45 if( options.log ) sout | "=== New connection" | fd | "" | ", waiting for requests ==="; 45 46 REQUEST: 46 47 for() { … … 53 54 size_t len = options.socket.buflen; 54 55 char buffer[len]; 55 if( options.log ) printf("=== Reading request ===\n");56 if( options.log ) sout | "=== Reading request ==="; 56 57 [code, closed, file, name_size] = http_read(fd, buffer, len, &this.cancel); 57 58 58 59 // if we are done, break out of the loop 59 if( closed ) { 60 if( options.log ) printf("=== Connection closed ===\n"); 61 close(fd); 62 continue CONNECTION; 63 } 60 if( closed ) break REQUEST; 64 61 65 62 // If this wasn't a request retrun 400 66 63 if( code != OK200 ) { 67 printf("=== Invalid Request : %d ===\n", code_val(code));64 sout | "=== Invalid Request :" | code_val(code) | "==="; 68 65 answer_error(fd, code); 69 66 continue REQUEST; … … 71 68 72 69 if(0 == strncmp(file, "plaintext", min(name_size, sizeof("plaintext") ))) { 73 if( options.log ) printf("=== Request for /plaintext ===\n");70 if( options.log ) sout | "=== Request for /plaintext ==="; 74 71 75 72 char text[] = "Hello, World!\n"; 76 73 77 74 // Send the header 78 answer_plain(fd, text, sizeof(text)); 75 int ret = answer_plain(fd, text, sizeof(text)); 76 if( ret == -ECONNRESET ) break REQUEST; 79 77 80 if( options.log ) printf("=== Answer sent ===\n");78 if( options.log ) sout | "=== Answer sent ==="; 81 79 continue REQUEST; 82 80 } 83 81 84 82 if(0 == strncmp(file, "ping", min(name_size, sizeof("ping") ))) { 85 if( options.log ) printf("=== Request for /ping ===\n");83 if( options.log ) sout | "=== Request for /ping ==="; 86 84 87 85 // Send the header 88 answer_empty(fd); 86 int ret = answer_empty(fd); 87 if( ret == -ECONNRESET ) break REQUEST; 89 88 90 if( options.log ) printf("=== Answer sent ===\n");89 if( options.log ) sout | "=== Answer sent ==="; 91 90 continue REQUEST; 92 91 } 93 92 94 if( options.log ) printf("=== Request for file %.*s ===\n", (int)name_size, file); 93 if( options.log ) { 94 sout | "=== Request for file " | nonl; 95 write(sout, file, name_size); 96 sout | " ==="; 97 } 98 99 if( !options.file_cache.path ) { 100 if( options.log ) { 101 sout | "=== File Not Found (" | nonl; 102 write(sout, file, name_size); 103 sout | ") ==="; 104 } 105 answer_error(fd, E405); 106 continue REQUEST; 107 } 95 108 96 109 // Get the fd from the file cache … … 101 114 // If we can't find the file, return 404 102 115 if( ans_fd < 0 ) { 103 printf("=== File Not Found ===\n"); 116 if( options.log ) { 117 sout | "=== File Not Found (" | nonl; 118 write(sout, file, name_size); 119 sout | ") ==="; 120 } 104 121 answer_error(fd, E404); 105 122 continue REQUEST; … … 107 124 108 125 // Send the header 109 answer_header(fd, count); 126 int ret = answer_header(fd, count); 127 if( ret == -ECONNRESET ) break REQUEST; 110 128 111 129 // Send the desired file 112 sendfile( this.pipe, fd, ans_fd, count); 130 ret = sendfile( this.pipe, fd, ans_fd, count); 131 if( ret == -ECONNRESET ) break REQUEST; 113 132 114 if( options.log ) printf("=== Answer sent ===\n");133 if( options.log ) sout | "=== Answer sent ==="; 115 134 } 135 136 if( options.log ) sout | "=== Connection closed ==="; 137 close(fd); 138 continue CONNECTION; 116 139 } 117 140 } -
doc/LaTeXmacros/common.tex
rb6a8b31 rd95969a 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Oct 5 09:34:46 202014 %% Update Count : 4 6413 %% Last Modified On : Sat Jan 23 09:06:39 2021 14 %% Update Count : 491 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 229 229 \usepackage{listings} % format program code 230 230 \usepackage{lstlang} 231 \usepackage{calc} % latex arithmetic 232 231 233 \makeatletter 232 233 234 \newcommand{\LstBasicStyle}[1]{{\lst@basicstyle{#1}}} 234 235 \newcommand{\LstKeywordStyle}[1]{{\lst@basicstyle{\lst@keywordstyle{#1}}}} … … 265 266 showlines=true, % show blank lines at end of code 266 267 aboveskip=4pt, % spacing above/below code block 267 belowskip=3pt, 268 belowskip=-2pt, 269 numberstyle=\footnotesize\sf, % numbering style 268 270 % replace/adjust listing characters that look bad in sanserif 269 271 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1 … … 293 295 language=CFA, 294 296 escapechar=\$, % LaTeX escape in CFA code 297 mathescape=false, % LaTeX math escape in CFA code $...$ 295 298 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 296 299 }% lstset -
doc/bibliography/pl.bib
rb6a8b31 rd95969a 688 688 title = {Asynchronous Exception Propagation in Blocked Tasks}, 689 689 booktitle = {4th International Workshop on Exception Handling (WEH.08)}, 690 o rganization= {16th International Symposium on the Foundations of Software Engineering (FSE 16)},690 optorganization= {16th International Symposium on the Foundations of Software Engineering (FSE 16)}, 691 691 address = {Atlanta, U.S.A}, 692 692 month = nov, … … 7246 7246 7247 7247 @inproceedings{Edelson92, 7248 keywords = {persistence, pointers},7248 keywords = {persistence, smart pointers}, 7249 7249 contributer = {pabuhr@plg}, 7250 7250 author = {Daniel R. Edelson}, … … 7256 7256 year = 1992, 7257 7257 pages = {1-19}, 7258 } 7259 7260 @incollection{smartpointers, 7261 keywords = {smart pointers}, 7262 contributer = {pabuhr@plg}, 7263 author = {Andrei Alexandrescu}, 7264 title = {Smart Pointers}, 7265 booktitle = {Modern C++ Design: Generic Programming and Design Patterns Applied}, 7266 publisher = {Addison-Wesley}, 7267 year = 2001, 7268 chapter = 7, 7269 optpages = {?-?}, 7258 7270 } 7259 7271 … … 8245 8257 } 8246 8258 8259 @misc{vistorpattern, 8260 keywords = {visitor pattern}, 8261 contributer = {pabuhr@plg}, 8262 key = {vistor pattern}, 8263 title = {vistor pattern}, 8264 year = 2020, 8265 note = {WikipediA}, 8266 howpublished= {\href{https://en.wikipedia.org/wiki/Visitor\_pattern} 8267 {https://\-en.wikipedia.org/\-wiki/\-Visitor\_pattern}}, 8268 } 8269 8247 8270 % W 8248 8271 -
doc/theses/andrew_beach_MMath/.gitignore
rb6a8b31 rd95969a 3 3 4 4 # Final Files: 5 thesis.pdf5 *.pdf 6 6 7 7 # The Makefile here is not generated. -
doc/theses/andrew_beach_MMath/Makefile
rb6a8b31 rd95969a 1 1 ### Makefile for Andrew Beach's Masters Thesis 2 2 3 DOC= thesis.pdf3 DOC=uw-ethesis.pdf 4 4 BUILD=out 5 5 TEXSRC=$(wildcard *.tex) … … 7 7 STYSRC=$(wildcard *.sty) 8 8 CLSSRC=$(wildcard *.cls) 9 TEXLIB= .: ${BUILD}:9 TEXLIB= .:../../LaTeXmacros:${BUILD}: 10 10 BIBLIB= .:../../bibliography 11 11 … … 29 29 ${LATEX} ${BASE} 30 30 ${BIBTEX} ${BUILD}/${BASE} 31 ${LATEX} ${BASE} 31 32 ${GLOSSARY} ${BUILD}/${BASE} 32 33 ${LATEX} ${BASE} -
doc/theses/andrew_beach_MMath/existing.tex
rb6a8b31 rd95969a 1 \chapter{\CFA{} Existing Features} 2 3 \section{Overloading and extern} 4 Cforall has overloading, allowing multiple definitions of the same name to 5 be defined. 6 7 This also adds name mangling so that the assembly symbols are unique for 8 different overloads. For compatability with names in C there is also a 9 syntax to diable the name mangling. These unmangled names cannot be overloaded 10 but act as the interface between C and \CFA code. 11 12 The syntax for disabling mangling is: 13 \begin{lstlisting} 14 extern "C" { 15 ... 16 } 17 \end{lstlisting} 18 19 To re-enable mangling once it is disabled the syntax is: 20 \begin{lstlisting} 21 extern "Cforall" { 22 ... 23 } 24 \end{lstlisting} 25 26 Both should occur at the declaration level and effect all the declarations 27 in \texttt{...}. Neither care about the state of mangling when they begin 28 and will return to that state after the group is finished. So re-enabling 29 is only used to nest areas of mangled and unmangled declarations. 30 31 \section{References} 32 \CFA adds references to C. These are auto-dereferencing pointers and use the 33 same syntax as pointers except they use ampersand (\codeCFA{\&}) instead of 34 the asterisk (\codeCFA{*}). They can also be constaint or mutable, if they 35 are mutable they may be assigned to by using the address-of operator 36 (\codeCFA\&) which converts them into a pointer. 1 \chapter{\texorpdfstring{\CFA Existing Features}{Cforall Existing Features}} 2 3 \CFA (C-for-all)~\cite{Cforall} is an open-source project extending ISO C with 4 modern safety and productivity features, while still ensuring backwards 5 compatibility with C and its programmers. \CFA is designed to have an 6 orthogonal feature-set based closely on the C programming paradigm 7 (non-object-oriented) and these features can be added incrementally to an 8 existing C code-base allowing programmers to learn \CFA on an as-needed basis. 9 10 Only those \CFA features pertinent to this thesis are discussed. Many of the 11 \CFA syntactic and semantic features used in the thesis should be fairly 12 obvious to the reader. 13 14 \section{\texorpdfstring{Overloading and \lstinline|extern|}{Overloading and extern}} 15 \CFA has extensive overloading, allowing multiple definitions of the same name 16 to be defined.~\cite{Moss18} 17 \begin{cfa} 18 char i; int i; double i; $\C[3.75in]{// variable overload}$ 19 int f(); double f(); $\C{// return overload}$ 20 void g( int ); void g( double ); $\C{// parameter overload}\CRT$ 21 \end{cfa} 22 This feature requires name mangling so the assembly symbols are unique for 23 different overloads. For compatibility with names in C, there is also a syntax 24 to disable name mangling. These unmangled names cannot be overloaded but act as 25 the interface between C and \CFA code. The syntax for disabling/enabling 26 mangling is: 27 \begin{cfa} 28 // name mangling 29 int i; // _X1ii_1 30 @extern "C"@ { // no name mangling 31 int j; // j 32 @extern "Cforall"@ { // name mangling 33 int k; // _X1ki_1 34 } 35 // no name mangling 36 } 37 // name mangling 38 \end{cfa} 39 Both forms of @extern@ affect all the declarations within their nested lexical 40 scope and transition back to the previous mangling state when the lexical scope 41 ends. 42 43 \section{Reference Type} 44 \CFA adds a rebindable reference type to C, but more expressive than the \CC 45 reference. Multi-level references are allowed and act like auto-dereferenced 46 pointers using the ampersand (@&@) instead of the pointer asterisk (@*@). \CFA 47 references may also be mutable or non-mutable. If mutable, a reference variable 48 may be assigned to using the address-of operator (@&@), which converts the 49 reference to a pointer. 50 \begin{cfa} 51 int i, j; 52 int @&@ ri = i, @&&@ rri = ri; 53 rri = 3; // auto-dereference assign to i 54 @&@ri = @&@j; // rebindable 55 ri = 5; // assign to j 56 \end{cfa} 37 57 38 58 \section{Constructors and Destructors} 39 59 40 60 Both constructors and destructors are operators, which means they are just 41 functions with special names. The special names are used to define them and 42 may be used to call the functions expicately. The \CFA special names are 43 constructed by taking the tokens in the operators and putting \texttt{?} where 44 the arguments would go. So multiplication is \texttt{?*?} while dereference 45 is \texttt{*?}. This also make it easy to tell the difference between 46 pre-fix operations (such as \texttt{++?}) and post-fix operations 47 (\texttt{?++}). 48 49 The special name for contructors is \texttt{?\{\}}, which comes from the 50 initialization syntax in C. The special name for destructors is 51 \texttt{\^{}?\{\}}. % I don't like the \^{} symbol but $^\wedge$ isn't better. 52 53 Any time a type T goes out of scope the destructor matching 54 \codeCFA{void ^?\{\}(T \&);} is called. In theory this is also true of 55 primitive types such as \codeCFA{int}, but in practice those are no-ops and 56 are usually omitted for optimization. 61 functions with special operator names rather than type names in \CC. The 62 special operator names may be used to call the functions explicitly (not 63 allowed in \CC for constructors). 64 65 In general, operator names in \CFA are constructed by bracketing an operator 66 token with @?@, which indicates where the arguments. For example, infixed 67 multiplication is @?*?@ while prefix dereference is @*?@. This syntax make it 68 easy to tell the difference between prefix operations (such as @++?@) and 69 post-fix operations (@?++@). 70 71 The special name for a constructor is @?{}@, which comes from the 72 initialization syntax in C. The special name for a destructor is @^{}@, where 73 the @^@ has no special meaning. 74 % I don't like the \^{} symbol but $^\wedge$ isn't better. 75 \begin{cfa} 76 struct T { ... }; 77 void ?@{}@(@T &@ this, ...) { ... } // constructor 78 void ?@^{}@(@T &@ this, ...) { ... } // destructor 79 { 80 T s = @{@ ... @}@; // same constructor/initialization braces 81 } // destructor call automatically generated 82 \end{cfa} 83 The first parameter is a reference parameter to the type for the 84 constructor/destructor. Destructors may have multiple parameters. The compiler 85 implicitly matches an overloaded constructor @void ^?{}(T &, ...);@ to an 86 object declaration with associated initialization, and generates a construction 87 call after the object is allocated. When an object goes out of scope, the 88 matching overloaded destructor @void ^?{}(T &);@ is called. Without explicit 89 definition, \CFA creates a default and copy constructor, destructor and 90 assignment (like \CC). It is possible to define constructors/destructors for 91 basic and existing types. 57 92 58 93 \section{Polymorphism} 59 \CFA uses polymorphism to create functions and types that are defined over 60 different types. \CFA polymorphic declarations serve the same role as \CPP 61 templates or Java generics. 62 63 Polymorphic declaractions start with a forall clause that goes before the 64 standard (monomorphic) declaration. These declarations have the same syntax 65 except that you may use the names introduced by the forall clause in them. 66 67 Forall clauses are written \codeCFA{forall( ... )} where \codeCFA{...} becomes 68 the list of polymorphic variables (local type names) and assertions, which 69 repersent required operations on those types. 70 71 \begin{lstlisting} 72 forall(dtype T | { void do_once(T &); }) 73 void do_twice(T & value) { 74 do_once(value); 75 do_once(value); 76 } 77 \end{lstlisting} 78 79 A polymorphic function can be used in the same way normal functions are. 80 The polymorphics variables are filled in with concrete types and the 81 assertions are checked. An assertion checked by seeing if that name of that 82 type (with all the variables replaced with the concrete types) is defined at 83 the the call site. 84 85 As an example, even if no function named \codeCFA{do_once} is not defined 86 near the definition of \codeCFA{do_twice} the following code will work. 87 \begin{lstlisting} 94 \CFA uses parametric polymorphism to create functions and types that are 95 defined over multiple types. \CFA polymorphic declarations serve the same role 96 as \CC templates or Java generics. The ``parametric'' means the polymorphism is 97 accomplished by passing argument operations to associate \emph{parameters} at 98 the call site, and these parameters are used in the function to differentiate 99 among the types the function operates on. 100 101 Polymorphic declarations start with a universal @forall@ clause that goes 102 before the standard (monomorphic) declaration. These declarations have the same 103 syntax except they may use the universal type names introduced by the @forall@ 104 clause. For example, the following is a polymorphic identity function that 105 works on any type @T@: 106 \begin{cfa} 107 @forall( T )@ @T@ identity( @T@ val ) { return val; } 108 int forty_two = identity( 42 ); // T bound to int, forty_two == 42 109 \end{cfa} 110 111 To allow a polymorphic function to be separately compiled, the type @T@ must be 112 constrained by the operations used on @T@ in the function body. The @forall@ 113 clauses is augmented with a list of polymorphic variables (local type names) 114 and assertions (constraints), which represent the required operations on those 115 types used in a function, \eg: 116 \begin{cfa} 117 forall( T @| { void do_once(T); }@) // assertion 118 void do_twice(T value) { 119 do_once(value); 120 do_once(value); 121 } 122 void do_once(int i) { ... } // provide assertion 123 int i; 124 do_twice(i); // implicitly pass assertion do_once to do_twice 125 \end{cfa} 126 Any object with a type fulfilling the assertion may be passed as an argument to 127 a @do_twice@ call. 128 129 A polymorphic function can be used in the same way as a normal function. The 130 polymorphic variables are filled in with concrete types and the assertions are 131 checked. An assertion is checked by verifying each assertion operation (with 132 all the variables replaced with the concrete types from the arguments) is 133 defined at a call site. 134 135 Note, a function named @do_once@ is not required in the scope of @do_twice@ to 136 compile it, unlike \CC template expansion. Furthermore, call-site inferencing 137 allows local replacement of the most specific parametric functions needs for a 138 call. 139 \begin{cfa} 140 void do_once(double y) { ... } // global 88 141 int quadruple(int x) { 89 void do_once(int & y) { 90 y = y * 2; 91 } 92 do_twice(x); 93 return x; 94 } 95 \end{lstlisting} 96 This is not the recommended way to implement a quadruple function but it 97 does work. The complier will deduce that \codeCFA{do_twice}'s T is an 98 integer from the argument. It will then look for a definition matching the 99 assertion which is the \codeCFA{do_once} defined within the function. That 100 function will be passed in as a function pointer to \codeCFA{do_twice} and 101 called within it. 102 103 To avoid typing out long lists of assertions again and again there are also 104 traits which collect assertions into convenent packages that can then be used 105 in assertion lists instead of all of their components. 106 \begin{lstlisting} 107 trait done_once(dtype T) { 108 void do_once(T &); 109 } 110 \end{lstlisting} 111 112 After this the forall list in the previous example could instead be written 113 with the trait instead of the assertion itself. 114 \begin{lstlisting} 115 forall(dtype T | done_once(T)) 116 \end{lstlisting} 117 118 Traits can have arbitrary number of assertions in them and are usually used to 119 create short hands for, and give descriptive names to, commond groupings of 120 assertions. 121 122 Polymorphic structures and unions may also be defined by putting a forall 123 clause before the declaration. The type variables work the same way except 124 are now used in field declaractions instead of parameters and local variables. 125 126 \begin{lstlisting} 142 void do_once(int y) { y = y * 2; } // local 143 do_twice(x); // using local "do_once" 144 return x; 145 } 146 \end{cfa} 147 Specifically, the complier deduces that @do_twice@'s T is an integer from the 148 argument @x@. It then looks for the most specific definition matching the 149 assertion, which is the nested integral @do_once@ defined within the 150 function. The matched assertion function is then passed as a function pointer 151 to @do_twice@ and called within it. 152 153 To avoid typing long lists of assertions, constraints can be collect into 154 convenient packages called a @trait@, which can then be used in an assertion 155 instead of the individual constraints. 156 \begin{cfa} 157 trait done_once(T) { 158 void do_once(T); 159 } 160 \end{cfa} 161 and the @forall@ list in the previous example is replaced with the trait. 162 \begin{cfa} 163 forall(dtype T | @done_once(T)@) 164 \end{cfa} 165 In general, a trait can contain an arbitrary number of assertions, both 166 functions and variables, and are usually used to create a shorthand for, and 167 give descriptive names to, common groupings of assertions describing a certain 168 functionality, like @sumable@, @listable@, \etc. 169 170 Polymorphic structures and unions are defined by qualifying the aggregate type 171 with @forall@. The type variables work the same except they are used in field 172 declarations instead of parameters, returns, and local variable declarations. 173 \begin{cfa} 127 174 forall(dtype T) 128 175 struct node { 129 node(T) * next; 130 T * data; 131 } 132 \end{lstlisting} 133 134 The \codeCFA{node(T)} is a use of a polymorphic structure. Polymorphic types 135 must be provided their polymorphic parameters. 136 137 There are many other features of polymorphism that have not given here but 138 these are the ones used by the exception system. 176 node(T) * next; // generic linked node 177 T * data; 178 } 179 \end{cfa} 180 The generic type @node(T)@ is an example of a polymorphic-type usage. Like \CC 181 templates usage, a polymorphic-type usage must specify a type parameter. 182 183 There are many other polymorphism features in \CFA but these are the ones used 184 by the exception system. 139 185 140 186 \section{Concurrency} 141 142 \CFA has a number of concurrency features, \codeCFA{thread}s, 143 \codeCFA{monitor}s and \codeCFA{mutex} parameters, \codeCFA{coroutine}s and 144 \codeCFA{generator}s. The two features that interact with the exception system 145 are \codeCFA{thread}s and \codeCFA{coroutine}s; they and their supporting 146 constructs will be described here. 147 148 \subsection{Coroutines} 149 Coroutines are routines that do not have to finish execution to hand control 150 back to their caller, instead they may suspend their execution at any time 151 and resume it later. 152 Coroutines are not true concurrency but share some similarities and many of 153 the same underpinnings and so are included as part of the \CFA threading 154 library. 155 156 In \CFA coroutines are created using the \codeCFA{coroutine} keyword which 157 works just like \codeCFA{struct} except that the created structure will be 158 modified by the compiler to satify the \codeCFA{is_coroutine} trait. 159 160 These structures act as the interface between callers and the coroutine, 161 the fields are used to pass information in and out. Here is a simple example 162 where the single field is used to pass the next number in a sequence out. 163 \begin{lstlisting} 187 \CFA has a number of concurrency features: @thread@, @monitor@, @mutex@ 188 parameters, @coroutine@ and @generator@. The two features that interact with 189 the exception system are @thread@ and @coroutine@; they and their supporting 190 constructs are described here. 191 192 \subsection{Coroutine} 193 A coroutine is a type with associated functions, where the functions are not 194 required to finish execution when control is handed back to the caller. Instead 195 they may suspend execution at any time and be resumed later at the point of 196 last suspension. (Generators are stackless and coroutines are stackful.) These 197 types are not concurrent but share some similarities along with common 198 underpinnings, so they are combined with the \CFA threading library. Further 199 discussion in this section only refers to the coroutine because generators are 200 similar. 201 202 In \CFA, a coroutine is created using the @coroutine@ keyword, which is an 203 aggregate type like @struct,@ except the structure is implicitly modified by 204 the compiler to satisfy the @is_coroutine@ trait; hence, a coroutine is 205 restricted by the type system to types that provide this special trait. The 206 coroutine structure acts as the interface between callers and the coroutine, 207 and its fields are used to pass information in and out of coroutine interface 208 functions. 209 210 Here is a simple example where a single field is used to pass (communicate) the 211 next number in a sequence. 212 \begin{cfa} 164 213 coroutine CountUp { 165 unsigned int next; 166 } 167 \end{lstlisting} 168 169 The routine part of the coroutine is a main function for the coroutine. It 170 takes a reference to a coroutine object and returns nothing. In this function, 171 and any functions called by this function, the suspend statement may be used 172 to return execution to the coroutine's caller. When control returns to the 173 function it continue from that same suspend statement instead of at the top 174 of the function. 175 \begin{lstlisting} 176 void main(CountUp & this) { 177 unsigned int next = 0; 178 while (true) { 179 this.next = next; 180 suspend; 181 next = next + 1; 182 } 183 } 184 \end{lstlisting} 185 186 Control is passed to the coroutine with the resume function. This includes the 187 first time when the coroutine is starting up. The resume function takes a 188 reference to the coroutine structure and returns the same reference. The 189 return value is for easy access to communication variables. For example the 190 next value from a count-up can be generated and collected in a single 191 expression: \codeCFA{resume(count).next}. 214 unsigned int next; // communication variable 215 } 216 CountUp countup; 217 \end{cfa} 218 Each coroutine has @main@ function, which takes a reference to a coroutine 219 object and returns @void@. 220 \begin{cfa}[numbers=left] 221 void main(@CountUp & this@) { // argument matches trait is_coroutine 222 unsigned int up = 0; // retained between calls 223 while (true) { 224 next = up; // make "up" available outside function 225 @suspend;@$\label{suspend}$ 226 up += 1; 227 } 228 } 229 \end{cfa} 230 In this function, or functions called by this function (helper functions), the 231 @suspend@ statement is used to return execution to the coroutine's caller 232 without terminating the coroutine. 233 234 A coroutine is resumed by calling the @resume@ function, \eg @resume(countup)@. 235 The first resume calls the @main@ function at the top. Thereafter, resume calls 236 continue a coroutine in the last suspended function after the @suspend@ 237 statement, in this case @main@ line~\ref{suspend}. The @resume@ function takes 238 a reference to the coroutine structure and returns the same reference. The 239 return value allows easy access to communication variables defined in the 240 coroutine object. For example, the @next@ value for coroutine object @countup@ 241 is both generated and collected in the single expression: 242 @resume(countup).next@. 192 243 193 244 \subsection{Monitors and Mutex} 194 195 True concurrency does not garrenty ordering. To get some of that ordering back 196 \CFA uses monitors and mutex (mutual exclution) parameters. A monitor is 197 another special declaration that contains a lock and is compatable with mutex 198 parameters. 199 200 Function parameters can have the \codeCFA{mutex} qualifiers on reference 201 arguments, for example \codeCFA{void example(a_monitor & mutex arg);}. When the 202 function is called it will acquire the lock on all of the mutex parameters. 203 204 This means that all functions that mutex on a type are part of a critical 205 section and only one will ever run at a time. 245 Concurrency does not guarantee ordering; without ordering results are 246 non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@ 247 (mutual exclusion) parameters. A monitor is another kind of aggregate, where 248 the compiler implicitly inserts a lock and instances are compatible with 249 @mutex@ parameters. 250 251 A function that requires deterministic (ordered) execution, acquires mutual 252 exclusion on a monitor object by qualifying an object reference parameter with 253 @mutex@. 254 \begin{cfa} 255 void example(MonitorA & @mutex@ argA, MonitorB & @mutex@ argB); 256 \end{cfa} 257 When the function is called, it implicitly acquires the monitor lock for all of 258 the mutex parameters without deadlock. This semantics means all functions with 259 the same mutex type(s) are part of a critical section for objects of that type 260 and only one runs at a time. 206 261 207 262 \subsection{Threads} 208 While coroutines allow new things to be done with a single execution path 209 threads actually introduce new paths of execution that continue independently. 210 Now for threads to work together their must be some communication between them 211 and that means the timing of certain operations does have to be known. There 212 or various means of syncronization and mutual exclution provided by \CFA but 213 for exceptions only the basic two -- fork and join -- are needed. 214 215 Threads are created like coroutines except the keyword is changed: 216 \begin{lstlisting} 263 Functions, generators, and coroutines are sequential so there is only a single 264 (but potentially sophisticated) execution path in a program. Threads introduce 265 multiple execution paths that continue independently. 266 267 For threads to work safely with objects requires mutual exclusion using 268 monitors and mutex parameters. For threads to work safely with other threads, 269 also requires mutual exclusion in the form of a communication rendezvous, which 270 also supports internal synchronization as for mutex objects. For exceptions 271 only the basic two basic operations are important: thread fork and join. 272 273 Threads are created like coroutines with an associated @main@ function: 274 \begin{cfa} 217 275 thread StringWorker { 218 const char * input;219 int result;276 const char * input; 277 int result; 220 278 }; 221 222 279 void main(StringWorker & this) { 223 const char * localCopy = this.input; 224 // ... do some work, perhaps hashing the string ... 225 this.result = result; 226 } 227 \end{lstlisting} 228 The main function will start executing after the fork operation and continue 229 executing until it is finished. If another thread joins with this one it will 230 wait until main has completed execution. In other words everything the thread 231 does is between fork and join. 232 233 From the outside this is the creation and destruction of the thread object. 234 Fork happens after the constructor is run and join happens before the 235 destructor runs. Join also happens during the \codeCFA{join} function which 236 can be used to join a thread earlier. If it is used the destructor does not 237 join as that has already been completed. 280 const char * localCopy = this.input; 281 // ... do some work, perhaps hashing the string ... 282 this.result = result; 283 } 284 { 285 StringWorker stringworker; // fork thread running in "main" 286 } // implicitly join with thread $\(\Rightarrow\)$ wait for completion 287 \end{cfa} 288 The thread main is where a new thread starts execution after a fork operation 289 and then the thread continues executing until it is finished. If another thread 290 joins with an executing thread, it waits until the executing main completes 291 execution. In other words, everything a thread does is between a fork and join. 292 293 From the outside, this behaviour is accomplished through creation and 294 destruction of a thread object. Implicitly, fork happens after a thread 295 object's constructor is run and join happens before the destructor runs. Join 296 can also be specified explicitly using the @join@ function to wait for a 297 thread's completion independently from its deallocation (\ie destructor 298 call). If @join@ is called explicitly, the destructor does not implicitly join. -
doc/theses/andrew_beach_MMath/features.tex
rb6a8b31 rd95969a 1 \chapter{Features} 2 3 This chapter covers the design and user interface of the \CFA exception 4 handling mechanism. 5 6 \section{Virtual Casts} 7 8 Virtual casts and virtual types are not truly part of the exception system but 9 they did not exist in \CFA and are useful in exceptions. So a minimal version 10 of they virtual system was designed and implemented. 11 12 Virtual types are organizied in simple hierarchies. Each virtual type may have 13 a parent and can have any number of children. A type's descendants are its 14 children and its children's descendants. A type may not be its own descendant. 15 16 Each virtual type has an associated virtual table type. A virtual table is a 17 structure that has fields for all the virtual members of a type. A virtual 18 type has all the virtual members of its parent and can add more. It may also 19 update the values of the virtual members. 20 21 Except for virtual casts, this is only used internally in the exception 22 system. There is no general purpose interface for the other features. A 23 a virtual cast has the following syntax: 24 25 \begin{lstlisting} 1 \chapter{Exception Features} 2 3 This chapter covers the design and user interface of the \CFA 4 exception-handling mechanism. 5 6 \section{Virtuals} 7 Virtual types and casts are not required for a basic exception-system but are 8 useful for advanced exception features. However, \CFA is not object-oriented so 9 there is no obvious concept of virtuals. Hence, to create advanced exception 10 features for this work, I needed to designed and implemented a virtual-like 11 system for \CFA. 12 13 Object-oriented languages often organized exceptions into a simple hierarchy, 14 \eg Java. 15 \begin{center} 16 \setlength{\unitlength}{4000sp}% 17 \begin{picture}(1605,612)(2011,-1951) 18 \put(2100,-1411){\vector(1, 0){225}} 19 \put(3450,-1411){\vector(1, 0){225}} 20 \put(3550,-1411){\line(0,-1){225}} 21 \put(3550,-1636){\vector(1, 0){150}} 22 \put(3550,-1636){\line(0,-1){225}} 23 \put(3550,-1861){\vector(1, 0){150}} 24 \put(2025,-1490){\makebox(0,0)[rb]{\LstBasicStyle{exception}}} 25 \put(2400,-1460){\makebox(0,0)[lb]{\LstBasicStyle{arithmetic}}} 26 \put(3750,-1460){\makebox(0,0)[lb]{\LstBasicStyle{underflow}}} 27 \put(3750,-1690){\makebox(0,0)[lb]{\LstBasicStyle{overflow}}} 28 \put(3750,-1920){\makebox(0,0)[lb]{\LstBasicStyle{zerodivide}}} 29 \end{picture}% 30 \end{center} 31 The hierarchy provides the ability to handle an exception at different degrees 32 of specificity (left to right). Hence, it is possible to catch a more general 33 exception-type in higher-level code where the implementation details are 34 unknown, which reduces tight coupling to the lower-level implementation. 35 Otherwise, low-level code changes require higher-level code changes, \eg, 36 changing from raising @underflow@ to @overflow@ at the low level means changing 37 the matching catch at the high level versus catching the general @arithmetic@ 38 exception. In detail, each virtual type may have a parent and can have any 39 number of children. A type's descendants are its children and its children's 40 descendants. A type may not be its own descendant. 41 42 The exception hierarchy allows a handler (@catch@ clause) to match multiple 43 exceptions, \eg a base-type handler catches both base and derived 44 exception-types. 45 \begin{cfa} 46 try { 47 ... 48 } catch(arithmetic &) { 49 ... // handle arithmetic, underflow, overflow, zerodivide 50 } 51 \end{cfa} 52 Most exception mechanisms perform a linear search of the handlers and select 53 the first matching handler, so the order of handers is now important because 54 matching is many to one. 55 56 Each virtual type needs an associated virtual table. A virtual table is a 57 structure with fields for all the virtual members of a type. A virtual type has 58 all the virtual members of its parent and can add more. It may also update the 59 values of the virtual members and often does. 60 61 While much of the virtual infrastructure is created, it is currently only used 62 internally for exception handling. The only user-level feature is the virtual 63 cast, which is the same as the \CC \lstinline[language=C++]|dynamic_cast|. 64 \begin{cfa} 26 65 (virtual TYPE)EXPRESSION 27 \end{lstlisting} 28 29 This has the same precidence as a traditional C-cast and can be used in the 30 same places. This will convert the result of EXPRESSION to the type TYPE. Both 31 the type of EXPRESSION and TYPE must be pointers to virtual types. 32 33 The cast is checked and will either return the original value or null, based 34 on the result of the check. The check is does the object pointed at have a 35 type that is a descendant of the target type. If it is the result is the 36 pointer, otherwise the result is null. 37 38 \section{Exceptions} 66 \end{cfa} 67 Note, the syntax and semantics matches a C-cast, rather than the unusual \CC 68 syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be a 69 pointer to a virtual type. The cast dynamically checks if the @EXPRESSION@ type 70 is the same or a subtype of @TYPE@, and if true, returns a pointer to the 71 @EXPRESSION@ object, otherwise it returns @0p@ (null pointer). 72 73 \section{Exception} 39 74 % Leaving until later, hopefully it can talk about actual syntax instead 40 75 % of my many strange macros. Syntax aside I will also have to talk about the 41 76 % features all exceptions support. 42 77 43 \section{Termination} 44 45 Termination exception throws are likely the most framilar kind, as they are 46 used in several popular programming languages. A termination will throw an 47 exception, search the stack for a handler, unwind the stack to where the 48 handler is defined, execute the handler and then continue execution after 49 the handler. They are used when execution cannot continue here. 50 51 Termination has two pieces of syntax it uses. The first is the throw: 52 \begin{lstlisting} 78 Exceptions are defined by the trait system; there are a series of traits, and 79 if a type satisfies them, then it can be used as an exception. The following 80 is the base trait all exceptions need to match. 81 \begin{cfa} 82 trait is_exception(exceptT &, virtualT &) { 83 virtualT const & @get_exception_vtable@(exceptT *); 84 }; 85 \end{cfa} 86 The function takes any pointer, including the null pointer, and returns a 87 reference to the virtual-table object. Defining this function also establishes 88 the virtual type and a virtual-table pair to the \CFA type-resolver and 89 promises @exceptT@ is a virtual type and a child of the base exception-type. 90 91 {\color{blue} PAB: I do not understand this paragraph.} 92 One odd thing about @get_exception_vtable@ is that it should always be a 93 constant function, returning the same value regardless of its argument. A 94 pointer or reference to the virtual table instance could be used instead, 95 however using a function has some ease of implementation advantages and allows 96 for easier disambiguation because the virtual type name (or the address of an 97 instance that is in scope) can be used instead of the mangled virtual table 98 name. Also note the use of the word ``promise'' in the trait 99 description. Currently, \CFA cannot check to see if either @exceptT@ or 100 @virtualT@ match the layout requirements. This is considered part of 101 @get_exception_vtable@'s correct implementation. 102 103 \section{Raise} 104 \CFA provides two kinds of exception raise: termination (see 105 \VRef{s:Termination}) and resumption (see \VRef{s:Resumption}), which are 106 specified with the following traits. 107 \begin{cfa} 108 trait is_termination_exception( 109 exceptT &, virtualT & | is_exception(exceptT, virtualT)) { 110 void @defaultTerminationHandler@(exceptT &); 111 }; 112 \end{cfa} 113 The function is required to allow a termination raise, but is only called if a 114 termination raise does not find an appropriate handler. 115 116 Allowing a resumption raise is similar. 117 \begin{cfa} 118 trait is_resumption_exception( 119 exceptT &, virtualT & | is_exception(exceptT, virtualT)) { 120 void @defaultResumptionHandler@(exceptT &); 121 }; 122 \end{cfa} 123 The function is required to allow a resumption raise, but is only called if a 124 resumption raise does not find an appropriate handler. 125 126 Finally there are three convenience macros for referring to the these traits: 127 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. Each 128 takes the virtual type's name, and for polymorphic types only, the 129 parenthesized list of polymorphic arguments. These macros do the name mangling 130 to get the virtual-table name and provide the arguments to both sides 131 {\color{blue}(PAB: What's a ``side''?)} 132 133 \subsection{Termination} 134 \label{s:Termination} 135 136 Termination raise, called ``throw'', is familiar and used in most programming 137 languages with exception handling. The semantics of termination is: search the 138 stack for a matching handler, unwind the stack frames to the matching handler, 139 execute the handler, and continue execution after the handler. Termination is 140 used when execution \emph{cannot} return to the throw. To continue execution, 141 the program must \emph{recover} in the handler from the failed (unwound) 142 execution at the raise to safely proceed after the handler. 143 144 A termination raise is started with the @throw@ statement: 145 \begin{cfa} 53 146 throw EXPRESSION; 54 \end{lstlisting} 55 56 The expression must evaluate to a reference to a termination exception. A 57 termination exception is any exception with a 58 \codeCFA{void defaultTerminationHandler(T &);} (the default handler) defined 59 on it. The handler is taken from the call sight with \CFA's trait system and 60 passed into the exception system along with the exception itself. 61 62 The exception passed into the system is then copied into managed memory. 63 This is to ensure it remains in scope during unwinding. It is the user's 64 responsibility to make sure the original exception is freed when it goes out 65 of scope. Being allocated on the stack is sufficient for this. 66 67 Then the exception system will search the stack starting from the throw and 68 proceding towards the base of the stack, from callee to caller. As it goes 69 it will check any termination handlers it finds: 70 71 \begin{lstlisting} 72 try { 73 TRY_BLOCK 74 } catch (EXCEPTION_TYPE * NAME) { 75 HANDLER 76 } 77 \end{lstlisting} 78 79 This shows a try statement with a single termination handler. The statements 80 in TRY\_BLOCK will be executed when control reaches this statement. While 81 those statements are being executed if a termination exception is thrown and 82 it is not handled by a try statement further up the stack the EHM will check 83 all of the terminations handlers attached to the try block, top to bottom. 84 85 At each handler the EHM will check to see if the thrown exception is a 86 descendant of EXCEPTION\_TYPE. If it is the pointer to the exception is 87 bound to NAME and the statements in HANDLER are executed. If control reaches 88 the end of the handler then it exits the block, the exception is freed and 89 control continues after the try statement. 90 91 The default handler is only used if no handler for the exception is found 92 after the entire stack is searched. When that happens the default handler 93 is called with a reference to the exception as its only argument. If the 94 handler returns control continues from after the throw statement. 95 96 \paragraph{Conditional Catches} 97 98 Catch clauses may also be written as: 99 \begin{lstlisting} 100 catch (EXCEPTION_TYPE * NAME ; CONDITION) 101 \end{lstlisting} 102 This has the same behaviour as a regular catch clause except that if the 103 exception matches the given type the condition is also run. If the result is 104 true only then is this considered a matching handler. If the result is false 105 then the handler does not match and the search continues with the next clause 106 in the try block. 107 108 The condition considers all names in scope at the beginning of the try block 109 to be in scope along with the name introduce in the catch clause itself. 110 111 \paragraph{Re-Throwing} 112 113 You can also rethrow the most recent termination exception with 114 \codeCFA{throw;}. % This is terrible and you should never do it. 115 This can be done in a handler or any function that could be called from a 116 handler. 117 118 This will start another termination throw reusing the exception, meaning it 119 does not copy the exception or allocated any more memory for it. However the 120 default handler is still at the original through and could refer to data that 121 was on the unwound section of the stack. So instead a new default handler that 122 does a program level abort is used. 123 124 \section{Resumption} 125 126 Resumption exceptions are less popular then termination but in many 127 regards are simpler and easier to understand. A resumption throws an exception, 128 searches for a handler on the stack, executes that handler on top of the stack 129 and then continues execution from the throw. These are used when a problem 130 needs to be fixed before execution continues. 131 132 A resumption is thrown with a throw resume statement: 133 \begin{lstlisting} 147 \end{cfa} 148 The expression must return a termination-exception reference, where the 149 termination exception has a type with a @void defaultTerminationHandler(T &)@ 150 (default handler) defined. The handler is found at the call site using \CFA's 151 trait system and passed into the exception system along with the exception 152 itself. 153 154 At runtime, a representation of the exception type and an instance of the 155 exception type is copied into managed memory (heap) to ensure it remains in 156 scope during unwinding. It is the user's responsibility to ensure the original 157 exception object at the throw is freed when it goes out of scope. Being 158 allocated on the stack is sufficient for this. 159 160 Then the exception system searches the stack starting from the throw and 161 proceeding towards the base of the stack, from callee to caller. At each stack 162 frame, a check is made for termination handlers defined by the @catch@ clauses 163 of a @try@ statement. 164 \begin{cfa} 165 try { 166 GUARDED_BLOCK 167 } @catch (EXCEPTION_TYPE$\(_1\)$ * NAME)@ { // termination handler 1 168 HANDLER_BLOCK$\(_1\)$ 169 } @catch (EXCEPTION_TYPE$\(_2\)$ * NAME)@ { // termination handler 2 170 HANDLER_BLOCK$\(_2\)$ 171 } 172 \end{cfa} 173 The statements in the @GUARDED_BLOCK@ are executed. If those statements, or any 174 functions invoked from those statements, throws an exception, and the exception 175 is not handled by a try statement further up the stack, the termination 176 handlers are searched for a matching exception type from top to bottom. 177 178 Exception matching checks the representation of the thrown exception-type is 179 the same or a descendant type of the exception types in the handler clauses. If 180 there is a match, a pointer to the exception object created at the throw is 181 bound to @NAME@ and the statements in the associated @HANDLER_BLOCK@ are 182 executed. If control reaches the end of the handler, the exception is freed, 183 and control continues after the try statement. 184 185 The default handler visible at the throw statement is used if no matching 186 termination handler is found after the entire stack is searched. At that point, 187 the default handler is called with a reference to the exception object 188 generated at the throw. If the default handler returns, the system default 189 action is executed, which often terminates the program. This feature allows 190 each exception type to define its own action, such as printing an informative 191 error message, when an exception is not handled in the program. 192 193 \subsection{Resumption} 194 \label{s:Resumption} 195 196 Resumption raise, called ``resume'', is as old as termination 197 raise~\cite{Goodenough75} but is less popular. In many ways, resumption is 198 simpler and easier to understand, as it is simply a dynamic call (as in 199 Lisp). The semantics of resumption is: search the stack for a matching handler, 200 execute the handler, and continue execution after the resume. Notice, the stack 201 cannot be unwound because execution returns to the raise point. Resumption is 202 used used when execution \emph{can} return to the resume. To continue 203 execution, the program must \emph{correct} in the handler for the failed 204 execution at the raise so execution can safely continue after the resume. 205 206 A resumption raise is started with the @throwResume@ statement: 207 \begin{cfa} 134 208 throwResume EXPRESSION; 135 \end{lstlisting} 136 The result of EXPRESSION must be a resumption exception type. A resumption 137 exception type is any type that satifies the assertion 138 \codeCFA{void defaultResumptionHandler(T &);} (the default handler). When the 139 statement is executed the expression is evaluated and the result is thrown. 140 141 Handlers are declared using clauses in try statements: 142 \begin{lstlisting} 143 try { 144 TRY_BLOCK 145 } catchResume (EXCEPTION_TYPE * NAME) { 146 HANDLER 147 } 148 \end{lstlisting} 149 This is a simple example with the try block and a single resumption handler. 150 Multiple resumption handlers can be put in a try statement and they can be 151 mixed with termination handlers. 152 153 When a resumption begins it will start searching the stack starting from 154 the throw statement and working its way to the callers. In each try statement 155 handlers will be tried top to bottom. Each handler is checked by seeing if 156 the thrown exception is a descendant of EXCEPTION\_TYPE. If not the search 157 continues. Otherwise NAME is bound to a pointer to the exception and the 158 HANDLER statements are executed. After they are finished executing control 159 continues from the throw statement. 160 161 If no approprate handler is found then the default handler is called. The 162 throw statement acts as a regular function call passing the exception to 163 the default handler and after the handler finishes executing control continues 164 from the throw statement. 165 166 The exception system also tracks the position of a search on the stack. If 167 another resumption exception is thrown while a resumption handler is running 168 it will first check handlers pushed to the stack by the handler and any 169 functions it called, then it will continue from the try statement that the 170 handler is a part of; except for the default handler where it continues from 171 the throw the default handler was passed to. 172 173 This makes the search pattern for resumption reflect the one for termination, 174 which is what most users expect. 175 176 % This might need a diagram. But it is an important part of the justifaction 209 \end{cfa} 210 The semantics of the @throwResume@ statement are like the @throw@, but the 211 expression has a type with a @void defaultResumptionHandler(T &)@ (default 212 handler) defined, where the handler is found at the call site by the type 213 system. At runtime, a representation of the exception type and an instance of 214 the exception type is \emph{not} copied because the stack is maintained during 215 the handler search. 216 217 Then the exception system searches the stack starting from the resume and 218 proceeding towards the base of the stack, from callee to caller. At each stack 219 frame, a check is made for resumption handlers defined by the @catchResume@ 220 clauses of a @try@ statement. 221 \begin{cfa} 222 try { 223 GUARDED_BLOCK 224 } @catchResume (EXCEPTION_TYPE$\(_1\)$ * NAME)@ { // resumption handler 1 225 HANDLER_BLOCK$\(_1\)$ 226 } @catchResume (EXCEPTION_TYPE$\(_2\)$ * NAME)@ { // resumption handler 2 227 HANDLER_BLOCK$\(_2\)$ 228 } 229 \end{cfa} 230 The statements in the @GUARDED_BLOCK@ are executed. If those statements, or any 231 functions invoked from those statements, resumes an exception, and the 232 exception is not handled by a try statement further up the stack, the 233 resumption handlers are searched for a matching exception type from top to 234 bottom. (Note, termination and resumption handlers may be intermixed in a @try@ 235 statement but the kind of raise (throw/resume) only matches with the 236 corresponding kind of handler clause.) 237 238 The exception search and matching for resumption is the same as for 239 termination, including exception inheritance. The difference is when control 240 reaches the end of the handler: the resumption handler returns after the resume 241 rather than after the try statement. The resume point assumes the handler has 242 corrected the problem so execution can safely continue. 243 244 Like termination, if no resumption handler is found, the default handler 245 visible at the resume statement is called, and the system default action is 246 executed. 247 248 For resumption, the exception system uses stack marking to partition the 249 resumption search. If another resumption exception is raised in a resumption 250 handler, the second exception search does not start at the point of the 251 original raise. (Remember the stack is not unwound and the current handler is 252 at the top of the stack.) The search for the second resumption starts at the 253 current point on the stack because new try statements may have been pushed by 254 the handler or functions called from the handler. If there is no match back to 255 the point of the current handler, the search skips the stack frames already 256 searched by the first resume and continues after the try statement. The default 257 handler always continues from default handler associated with the point where 258 the exception is created. 259 260 % This might need a diagram. But it is an important part of the justification 177 261 % of the design of the traversal order. 178 It also avoids the recursive resumption problem. If the entire stack is 179 searched loops of resumption can form. Consider a handler that handles an 180 exception of type A by resuming an exception of type B and on the same stack, 181 later in the search path, is a second handler that handles B by resuming A. 182 183 Assuming no other handlers on the stack handle A or B then in either traversal 184 system an A resumed from the top of the stack will be handled by the first 185 handler. A B resumed from the top or from the first handler it will be handled 186 by the second hander. The only difference is when A is thrown from the second 187 handler. The entire stack search will call the first handler again, creating a 188 loop. Starting from the position in the stack though will break this loop. 189 190 \paragraph{Conditional Catches} 191 192 Resumption supports conditional catch clauses like termination does. They 193 use the same syntax except the keyword is changed: 194 \begin{lstlisting} 195 catchResume (EXCEPTION_TYPE * NAME ; CONDITION) 196 \end{lstlisting} 197 198 It also has the same behaviour, after the exception type has been matched 199 with the EXCEPTION\_TYPE the CONDITION is evaluated with NAME in scope. If 200 the result is true then the hander is run, otherwise the search continues 201 just as if there had been a type mismatch. 202 203 \paragraph{Re-Throwing} 204 205 You may also re-throw resumptions with a \codeCFA{throwResume;} statement. 206 This can only be done from inside of a \codeCFA{catchResume} block. 207 208 Outside of any side effects of any code already run in the handler this will 209 have the same effect as if the exception had not been caught in the first 210 place. 262 \begin{verbatim} 263 throwResume2 ----------. 264 | | 265 generated from handler | 266 | | 267 handler | 268 | | 269 throwResume1 -----. : 270 | | : 271 try | : search skip 272 | | : 273 catchResume <----' : 274 | | 275 \end{verbatim} 276 277 This resumption search-pattern reflect the one for termination, which matches 278 with programmer expectations. However, it avoids the \emph{recursive 279 resumption} problem. If parts of the stack are searched multiple times, loops 280 can easily form resulting in infinite recursion. 281 282 Consider the trivial case: 283 \begin{cfa} 284 try { 285 throwResume$\(_1\)$ (E &){}; 286 } catch( E * ) { 287 throwResume; 288 } 289 \end{cfa} 290 Based on termination semantics, programmer expectation is for the re-resume to 291 continue searching the stack frames after the try statement. However, the 292 current try statement is still on the stack below the handler issuing the 293 reresume (see \VRef{s:Reraise}). Hence, the try statement catches the re-raise 294 again and does another re-raise \emph{ad infinitum}, which is confusing and 295 difficult to debug. The \CFA resumption search-pattern skips the try statement 296 so the reresume search continues after the try, mathcing programmer 297 expectation. 298 299 \section{Conditional Catch} 300 Both termination and resumption handler-clauses may perform conditional matching: 301 \begin{cfa} 302 catch (EXCEPTION_TYPE * NAME ; @CONDITION@) 303 \end{cfa} 304 First, the same semantics is used to match the exception type. Second, if the 305 exception matches, @CONDITION@ is executed. The condition expression may 306 reference all names in scope at the beginning of the try block and @NAME@ 307 introduced in the handler clause. If the condition is true, then the handler 308 matches. Otherwise, the exception search continues at the next appropriate kind 309 of handler clause in the try block. 310 \begin{cfa} 311 try { 312 f1 = open( ... ); 313 f2 = open( ... ); 314 ... 315 } catch( IOFailure * f ; fd( f ) == f1 ) { 316 // only handle IO failure for f1 317 } 318 \end{cfa} 319 Note, catching @IOFailure@, checking for @f1@ in the handler, and reraising the 320 exception if not @f1@ is different because the reraise does not examine any of 321 remaining handlers in the current try statement. 322 323 \section{Reraise} 324 \label{s:Reraise} 325 Within the handler block or functions called from the handler block, it is 326 possible to reraise the most recently caught exception with @throw@ or 327 @throwResume@, respective. 328 \begin{cfa} 329 catch( ... ) { 330 ... throw; // rethrow 331 } catchResume( ... ) { 332 ... throwResume; // reresume 333 } 334 \end{cfa} 335 The only difference between a raise and a reraise is that reraise does not 336 create a new exception; instead it continues using the current exception, \ie 337 no allocation and copy. However the default handler is still set to the one 338 visible at the raise point, and hence, for termination could refer to data that 339 is part of an unwound stack frame. To prevent this problem, a new default 340 handler is generated that does a program-level abort. 341 211 342 212 343 \section{Finally Clauses} 213 214 A \codeCFA{finally} clause may be placed at the end of a try statement after 215 all the handler clauses. In the simply case, with no handlers, it looks like 216 this: 217 218 \begin{lstlisting} 219 try { 220 TRY_BLOCK 344 A @finally@ clause may be placed at the end of a @try@ statement. 345 \begin{cfa} 346 try { 347 GUARDED_BLOCK 348 } ... // any number or kind of handler clauses 221 349 } finally { 222 FINAL_STATEMENTS 223 } 224 \end{lstlisting} 225 226 Any number of termination handlers and resumption handlers may proceed the 227 finally clause. 228 229 The FINAL\_STATEMENTS, the finally block, are executed whenever the try 230 statement is removed from the stack. This includes: the TRY\_BLOCK finishes 231 executing, a termination exception finishes executing and the stack unwinds. 232 233 Execution of the finally block should finish by letting control run off 234 the end of the block. This is because after the finally block is complete 235 control will continue to where ever it would if the finally clause was not 236 present. 237 238 Because of this local control flow out of the finally block is forbidden. 239 The compiler rejects uses of \codeCFA{break}, \codeCFA{continue}, 240 \codeCFA{fallthru} and \codeCFA{return} that would cause control to leave 241 the finally block. Other ways to leave the finally block - such as a long 242 jump or termination - are much harder to check, at best requiring additional 243 runtime overhead, and so are merely discouraged. 350 FINALLY_BLOCK 351 } 352 \end{cfa} 353 The @FINALLY_BLOCK@ is executed when the try statement is unwound from the 354 stack, \ie when the @GUARDED_BLOCK@ or any handler clause finishes. Hence, the 355 finally block is always executed. 356 357 Execution of the finally block should always finish, meaning control runs off 358 the end of the block. This requirement ensures always continues as if the 359 finally clause is not present, \ie finally is for cleanup not changing control 360 flow. Because of this requirement, local control flow out of the finally block 361 is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or 362 @return@ that causes control to leave the finally block. Other ways to leave 363 the finally block, such as a long jump or termination are much harder to check, 364 and at best requiring additional run-time overhead, and so are discouraged. 244 365 245 366 \section{Cancellation} 246 247 Cancellation can be thought of as a stack-level abort or as an uncatchable 248 termination. It unwinds the entirety of the current exception and if possible 249 passes an exception to a different stack as a message. 250 251 There is no special statement for starting a cancellation, instead you call 252 the standard libary function \codeCFA{cancel\_stack} which takes an exception. 253 Unlike in a throw this exception is not used in control flow but is just there 254 to pass information about why the cancellation happened. 255 256 The handler is decided entirely by which stack is being cancelled. There are 257 three handlers that apply to three different groups of stacks: 258 \begin{itemize} 259 \item Main Stack: 260 The main stack is the one on which the program main is called at the beginning 261 of your program. It is also the only stack you have without the libcfathreads. 262 263 Because of this there is no other stack ``above" (or possibly at all) for main 264 to notify when a cancellation occurs. So after the stack is unwound we do a 265 program level abort. 266 267 \item Thread Stack: 268 Thread stacks are those created \codeCFA{thread} or otherwise satify the 269 \codeCFA{is\_thread} trait. 270 271 Threads only have two structural points of communication that must happen, 272 start and join. As the thread must be running to preform a cancellation it 273 will be after start and before join, so join is one cancellation uses. 274 275 After the stack is unwound the thread will halt as if had completed normally 276 and wait for another thread to join with it. The other thread, when it joins, 277 checks for a cancellation. If so it will throw the resumption exception 278 \codeCFA{ThreadCancelled}. 279 280 There is a difference here in how explicate joins (with the \codeCFA{join} 281 function) and implicate joins (from a destructor call). Explicate joins will 282 take the default handler (\codeCFA{defaultResumptionHandler}) from the context 283 and use like a regular through does if the exception is not caught. The 284 implicate join does a program abort instead. 285 286 This is for safety. One of the big problems in exceptions is you cannot handle 287 two terminations or cancellations on the same stack as either can destroy the 288 context required for the other. This can happen with join but as the 289 destructors will always be run when the stack is being unwound and one 290 termination/cancellation is already active. Also since they are implicite they 291 are easier to forget about. 292 293 \item Coroutine Stack: 294 Coroutine stacks are those created with \codeCFA{coroutine} or otherwise 295 satify the \codeCFA{is\_coroutine} trait. 296 297 A coroutine knows of two other coroutines, its starter and its last resumer. 298 The last resumer is ``closer" so that is the one notified. 299 300 After the stack is unwound control goes to the last resumer. 301 Resume will resume throw a \codeCFA{CoroutineCancelled} exception, which is 302 polymorphic over the coroutine type and has a pointer to the coroutine being 303 cancelled and the cancelling exception. The resume function also has an 304 assertion that the \codeCFA{defaultResumptionHandler} for the exception. So it 305 will use the default handler like a regular throw. 306 307 \end{itemize} 367 Cancellation is a stack-level abort, which can be thought of as as an 368 uncatchable termination. It unwinds the entirety of the current stack, and if 369 possible forwards the cancellation exception to a different stack. 370 371 There is no special statement for starting a cancellation; instead the standard 372 library function @cancel_stack@ is called passing an exception. Unlike a 373 raise, this exception is not used in matching only to pass information about 374 the cause of the cancellation. 375 376 Handling of a cancellation depends on which stack is being cancelled. 377 \begin{description} 378 \item[Main Stack:] 379 380 The main stack is the one used by the program main at the start of execution, 381 and is the only stack in a sequential program. Hence, when cancellation is 382 forwarded to the main stack, there is no other forwarding stack, so after the 383 stack is unwound, there is a program-level abort. 384 385 \item[Thread Stack:] 386 A thread stack is created for a @thread@ object or object that satisfies the 387 @is_thread@ trait. A thread only has two points of communication that must 388 happen: start and join. As the thread must be running to perform a 389 cancellation, it must occur after start and before join, so join is a 390 cancellation point. After the stack is unwound, the thread halts and waits for 391 another thread to join with it. The joining thread, checks for a cancellation, 392 and if present, resumes exception @ThreadCancelled@. 393 394 There is a subtle difference between the explicit join (@join@ function) and 395 implicit join (from a destructor call). The explicit join takes the default 396 handler (@defaultResumptionHandler@) from its calling context, which is used if 397 the exception is not caught. The implicit join does a program abort instead. 398 399 This semantics is for safety. One difficult problem for any exception system is 400 defining semantics when an exception is raised during an exception search: 401 which exception has priority, the original or new exception? No matter which 402 exception is selected, it is possible for the selected one to disrupt or 403 destroy the context required for the other. {\color{blue} PAB: I do not 404 understand the following sentences.} This loss of information can happen with 405 join but as the thread destructor is always run when the stack is being unwound 406 and one termination/cancellation is already active. Also since they are 407 implicit they are easier to forget about. 408 409 \item[Coroutine Stack:] A coroutine stack is created for a @coroutine@ object 410 or object that satisfies the @is_coroutine@ trait. A coroutine only knows of 411 two other coroutines, its starter and its last resumer. The last resumer has 412 the tightest coupling to the coroutine it activated. Hence, cancellation of 413 the active coroutine is forwarded to the last resumer after the stack is 414 unwound, as the last resumer has the most precise knowledge about the current 415 execution. When the resumer restarts, it resumes exception 416 @CoroutineCancelled@, which is polymorphic over the coroutine type and has a 417 pointer to the cancelled coroutine. 418 419 The resume function also has an assertion that the @defaultResumptionHandler@ 420 for the exception. So it will use the default handler like a regular throw. 421 \end{description} -
doc/theses/andrew_beach_MMath/future.tex
rb6a8b31 rd95969a 8 8 parts of the exception system that use the current version. 9 9 10 For instance a full virtual system would probably allow for several 11 improvements to the exception traits. Although they do currently work they 12 could be made easier to use by making the virtual table type implitate in the 13 trait (which would remove the need for those wrapper marcos) or allowing 14 for assertions that give the layout of a virtual table for safety. 10 There are several improvements to the virtual system that would improve 11 the exception traits. The biggest one is an assertion that checks that one 12 virtual type is a child of another virtual type. This would capture many of 13 the requirements much more precisely. 14 15 The full virtual system might also include other improvement like associated 16 types. This is a proposed feature that would allow traits to refer to types 17 not listed in their header. This would allow the exception traits to not 18 refer to the virtual table type explicatly which would remove the need for 19 the interface macros. 15 20 16 21 \section{Additional Throws} 17 Several other kinds of throws, beyond the termination throw ( \codeCFA{throw}),18 the resumption throw ( \codeCFA{throwResume}) and the re-throws, were considered.22 Several other kinds of throws, beyond the termination throw (@throw@), 23 the resumption throw (@throwResume@) and the re-throws, were considered. 19 24 None were as useful as the core throws but they would likely be worth 20 25 revising. … … 65 70 66 71 Also new techniques to skip previously searched parts of the stack will have 67 to be developed. 72 to be developed. The recursive resume problem still remains and ideally the 73 same pattern of ignoring sections of the stack. 68 74 69 \section{Support for More Platforms} 70 Termination is not portable because it is implemented with inline assembly. 71 Those sections will have to be rewritten to support different architectures 75 \section{Signal Exceptions} 76 Exception Handling: Issues and a Proposed Notation suggests there are three 77 types of exceptions: escape, notify and signal. 78 Escape exceptions are our termination exceptions, notify exceptions are 79 resumption exceptions and that leaves signal exception unimplemented. 72 80 73 \section{Quality-of-Life Improvements} 74 Finally come various improvements to the usability of \CFA. Most of these 75 would just require time. Time that would not lead to interesting research so 76 it has been left aside for now. A few examples are included here but there 77 are more: 81 Signal exceptions allow either behaviour, that is after the exception is 82 handled control can either return to the throw or from where the handler is 83 defined. 84 85 The design should be rexamined and be updated for \CFA. A very direct 86 translation would perhaps have a new throw and catch pair and a statement 87 (or statements) could be used to decide if the handler returns to the throw 88 or continues where it is, but there are other options. 89 90 For instance resumption could be extended to cover this use by allowing 91 local control flow out of it. This would require an unwind as part of the 92 transition as there are stack frames that have to be removed. 93 This would mean there is no notify like throw but because \CFA does not have 94 exception signatures a termination can be thrown from any resumption handler 95 already so there are already ways one could try to do this in existing \CFA. 96 97 % Maybe talk about the escape; and escape CONTROL_STMT; statements or how 98 % if we could choose if _Unwind_Resume proceeded to the clean-up stage this 99 % would be much easier to implement. 100 101 \section{Language Improvements} 102 There is also a lot of work that are not follow ups to this work in terms of 103 research, some have no interesting research to be done at all, but would 104 improve \CFA as a programming language. The full list of these would 105 naturally be quite extensive but here are a few examples that involve 106 exceptions: 78 107 79 108 \begin{itemize} 109 \item The implementation of termination is not portable because it includes 110 some assembly statements. These sections will have to be re-written to so 111 \CFA has full support on more machines. 80 112 \item Allowing exception handler to bind the exception to a reference instead 81 113 of a pointer. This should actually result in no change in behaviour so there 82 114 is no reason not to allow it. It is however a small improvement; giving a bit 83 115 of flexibility to the user in what style they want to use. 84 \item Enabling local control flow (by \codeCFA{break}, \codeCFA{return}and116 \item Enabling local control flow (by @break@, @return@ and 85 117 similar statements) out of a termination handler. The current set-up makes 86 118 this very difficult but the catch function that runs the handler after it has … … 88 120 much easier. (To do the same for try blocks would probably wait for zero-cost 89 121 exceptions, which would allow the try block to be inlined as well.) 90 \item Enabling local control flow out of a resumption handler. This would be91 a weighty operation, causing a stack unwind like a termination, so there might92 be a different statement or a statement modifier to make sure the user does93 this purposefully.94 95 However this would require the more complex system as they cannot be inlined96 into the original function as they can be run at a different place on the97 stack. So instead the unwinding will have to carry with it information on98 which one of these points to continue at and possibly also the return value99 for the function if a \codeCFA{return} statement was used.100 122 \end{itemize} -
doc/theses/andrew_beach_MMath/implement.tex
rb6a8b31 rd95969a 9 9 10 10 All of this is accessed through a field inserted at the beginning of every 11 virtual type. Currently it is called \codeC{virtual_table}but it is not11 virtual type. Currently it is called @virtual_table@ but it is not 12 12 ment to be accessed by the user. This field is a pointer to the type's 13 13 virtual table instance. It is assigned once during the object's construction … … 40 40 using that to calculate the mangled name of the parent's virtual table type. 41 41 There are two special fields that are included like normal fields but have 42 special initialization rules: the \codeC{size}field is the type's size and is43 initialized with a sizeof expression, the \codeC{align}field is the type's42 special initialization rules: the @size@ field is the type's size and is 43 initialized with a sizeof expression, the @align@ field is the type's 44 44 alignment and uses an alignof expression. The remaining fields are resolved 45 45 to a name matching the field's name and type using the normal visibility … … 56 56 The declarations include the virtual type definition and forward declarations 57 57 of the virtual table instance, constructor, message function and 58 \codeCFA{get_exception_vtable}. The definition includes the storage and58 @get_exception_vtable@. The definition includes the storage and 59 59 initialization of the virtual table instance and the bodies of the three 60 60 functions. … … 65 65 from the per-instance information. The virtual table type and most of the 66 66 functions are polymorphic so they are all part of the core. The virtual table 67 instance and the \codeCFA{get_exception_vtable}function.68 69 Coroutines and threads need instances of \codeCFA{CoroutineCancelled}and70 \codeCFA{ThreadCancelled}respectively to use all of their functionality.71 When a new data type is declared with \codeCFA{coroutine} or \codeCFA{thread}67 instance and the @get_exception_vtable@ function. 68 69 Coroutines and threads need instances of @CoroutineCancelled@ and 70 @ThreadCancelled@ respectively to use all of their functionality. 71 When a new data type is declared with @coroutine@ or @thread@ 72 72 the forward declaration for the instance is created as well. The definition 73 73 of the virtual table is created at the definition of the main function. … … 79 79 function. 80 80 81 The function is \codeC{__cfa__virtual_cast}and it is implemented in the81 The function is @__cfa__virtual_cast@ and it is implemented in the 82 82 standard library. It takes a pointer to the target type's virtual table and 83 83 the object pointer being cast. The function is very simple, getting the … … 87 87 88 88 For the generated code a forward decaration of the virtual works as follows. 89 There is a forward declaration of \codeC{__cfa__virtual_cast}in every cfa89 There is a forward declaration of @__cfa__virtual_cast@ in every cfa 90 90 file so it can just be used. The object argument is the expression being cast 91 91 so that is just placed in the argument list. … … 110 110 often across functions. 111 111 112 At a very basic level this can be done with \codeC{setjmp} \& \codeC{longjmp}112 At a very basic level this can be done with @setjmp@ \& @longjmp@ 113 113 which simply move the top of the stack, discarding everything on the stack 114 114 above a certain point. However this ignores all the clean-up code that should … … 118 118 both of these problems. 119 119 120 Libunwind, provided in \texttt{unwind.h}on most platorms, is a C library120 Libunwind, provided in @unwind.h@ on most platorms, is a C library 121 121 that provides \CPP style stack unwinding. Its operation is divided into two 122 122 phases. The search phase -- phase 1 -- is used to scan the stack and decide … … 142 142 143 143 GCC will generate an LSDA and attach its personality function with the 144 \texttt{-fexceptions}flag. However this only handles the cleanup attribute.144 @-fexceptions@ flag. However this only handles the cleanup attribute. 145 145 This attribute is used on a variable and specifies a function that should be 146 146 run when the variable goes out of scope. The function is passed a pointer to … … 165 165 messages for special cases (some of which should never be used by the 166 166 personality function) and error codes but unless otherwise noted the 167 personality function should always return \codeC{_URC_CONTINUE_UNWIND}.168 169 The \codeC{version}argument is the verson of the implementation that is167 personality function should always return @_URC_CONTINUE_UNWIND@. 168 169 The @version@ argument is the verson of the implementation that is 170 170 calling the personality function. At this point it appears to always be 1 and 171 171 it will likely stay that way until a new version of the API is updated. 172 172 173 The \codeC{action}argument is set of flags that tell the personality173 The @action@ argument is set of flags that tell the personality 174 174 function when it is being called and what it must do on this invocation. 175 175 The flags are as follows: 176 176 \begin{itemize} 177 \item \codeC{_UA_SEARCH_PHASE}: This flag is set whenever the personality177 \item@_UA_SEARCH_PHASE@: This flag is set whenever the personality 178 178 function is called during the search phase. The personality function should 179 179 decide if unwinding will stop in this function or not. If it does then the 180 personality function should return \codeC{_URC_HANDLER_FOUND}.181 \item \codeC{_UA_CLEANUP_PHASE}: This flag is set whenever the personality180 personality function should return @_URC_HANDLER_FOUND@. 181 \item@_UA_CLEANUP_PHASE@: This flag is set whenever the personality 182 182 function is called during the cleanup phase. If no other flags are set this 183 183 means the entire frame will be unwound and all cleanup code should be run. 184 \item \codeC{_UA_HANDLER_FRAME}: This flag is set during the cleanup phase184 \item@_UA_HANDLER_FRAME@: This flag is set during the cleanup phase 185 185 on the function frame that found the handler. The personality function must 186 186 prepare to return to normal code execution and return 187 \codeC{_URC_INSTALL_CONTEXT}.188 \item \codeC{_UA_FORCE_UNWIND}: This flag is set if the personality function187 @_URC_INSTALL_CONTEXT@. 188 \item@_UA_FORCE_UNWIND@: This flag is set if the personality function 189 189 is called through a forced unwind call. Forced unwind only performs the 190 190 cleanup phase and uses a different means to decide when to stop. See its … … 192 192 \end{itemize} 193 193 194 The \codeC{exception_class} argument is a copy of the \codeC{exception}'s195 \codeC{exception_class}field.196 197 The \codeC{exception}argument is a pointer to the user provided storage194 The @exception_class@ argument is a copy of the @exception@'s 195 @exception_class@ field. 196 197 The @exception@ argument is a pointer to the user provided storage 198 198 object. It has two public fields, the exception class which is actually just 199 199 a number that identifies the exception handling mechanism that created it and … … 201 201 exception needs to 202 202 203 The \codeC{context}argument is a pointer to an opaque type. This is passed203 The @context@ argument is a pointer to an opaque type. This is passed 204 204 to the many helper functions that can be called inside the personality 205 205 function. … … 218 218 functions traversing the stack new-to-old until a function finds a handler or 219 219 the end of the stack is reached. In the latter case raise exception will 220 return with \codeC{_URC_END_OF_STACK}.220 return with @_URC_END_OF_STACK@. 221 221 222 222 Once a handler has been found raise exception continues onto the the cleanup … … 227 227 228 228 If an error is encountered raise exception will return either 229 \codeC{_URC_FATAL_PHASE1_ERROR} or \codeC{_URC_FATAL_PHASE2_ERROR}depending229 @_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending 230 230 on when the error occured. 231 231 … … 259 259 been unwound. 260 260 261 Each time it is called the stop function should return \codeC{_URC_NO_REASON}261 Each time it is called the stop function should return @_URC_NO_REASON@ 262 262 or transfer control directly to other code outside of libunwind. The 263 263 framework does not provide any assistance here. 264 264 265 265 Its arguments are the same as the paired personality function. 266 The actions \codeC{_UA_CLEANUP_PHASE} and \codeC{_UA_FORCE_UNWIND}are always266 The actions @_UA_CLEANUP_PHASE@ and @_UA_FORCE_UNWIND@ are always 267 267 set when it is called. By the official standard that is all but both GCC and 268 268 Clang add an extra action on the last call at the end of the stack: 269 \codeC{_UA_END_OF_STACK}.269 @_UA_END_OF_STACK@. 270 270 271 271 \section{Exception Context} … … 280 280 Each stack has its own exception context. In a purely sequental program, using 281 281 only core Cforall, there is only one stack and the context is global. However 282 if the library \texttt{libcfathread}is linked then there can be multiple282 if the library @libcfathread@ is linked then there can be multiple 283 283 stacks so they will each need their own. 284 284 285 285 To handle this code always gets the exception context from the function 286 \codeC{this_exception_context}. The main exception handling code is in287 \texttt{libcfa}and that library also defines the function as a weak symbol288 so it acts as a default. Meanwhile in \texttt{libcfathread}the function is286 @this_exception_context@. The main exception handling code is in 287 @libcfa@ and that library also defines the function as a weak symbol 288 so it acts as a default. Meanwhile in @libcfathread@ the function is 289 289 defined as a strong symbol that replaces it when the libraries are linked 290 290 together. 291 291 292 The version of the function defined in \texttt{libcfa}is very simple. It292 The version of the function defined in @libcfa@ is very simple. It 293 293 returns a pointer to a global static variable. With only one stack this 294 294 global instance is associated with the only stack. 295 295 296 The version of the function defined in \texttt{libcfathread}has to handle296 The version of the function defined in @libcfathread@ has to handle 297 297 more as there are multiple stacks. The exception context is included as 298 298 part of the per-stack data stored as part of coroutines. In the cold data 299 299 section, stored at the base of each stack, is the exception context for that 300 stack. The \codeC{this_exception_context}uses the concurrency library to get300 stack. The @this_exception_context@ uses the concurrency library to get 301 301 the current coroutine and through it the cold data section and the exception 302 302 context. … … 323 323 to store the exception. Macros with pointer arthritic and type cast are 324 324 used to move between the components or go from the embedded 325 \codeC{_Unwind_Exception}to the entire node.325 @_Unwind_Exception@ to the entire node. 326 326 327 327 All of these nodes are strung together in a linked list. One linked list per … … 347 347 C which is what the \CFA compiler outputs so a work-around is used. 348 348 349 This work around is a function called \codeC{__cfaehm_try_terminate}in the349 This work around is a function called @__cfaehm_try_terminate@ in the 350 350 standard library. The contents of a try block and the termination handlers 351 351 are converted into functions. These are then passed to the try terminate … … 385 385 386 386 These nested functions and all other functions besides 387 \codeC{__cfaehm_try_terminate}in \CFA use the GCC personality function and388 the \texttt{-fexceptions}flag to generate the LSDA. This allows destructors387 @__cfaehm_try_terminate@ in \CFA use the GCC personality function and 388 the @-fexceptions@ flag to generate the LSDA. This allows destructors 389 389 to be implemented with the cleanup attribute. 390 390 … … 401 401 402 402 The handler function does both the matching and catching. It tries each 403 the condition of \codeCFA{catchResume}in order, top-to-bottom and until it403 the condition of @catchResume@ in order, top-to-bottom and until it 404 404 finds a handler that matches. If no handler matches then the function returns 405 405 false. Otherwise the matching handler is run, if it completes successfully 406 the function returns true. Rethrows, through the \codeCFA{throwResume;}406 the function returns true. Rethrows, through the @throwResume;@ 407 407 statement, cause the function to return true. 408 409 % Recursive Resumption Stuff: 410 Blocking out part of the stack is accomplished by updating the front of the 411 list as the search continues. Before the handler at a node is called the head 412 of the list is updated to the next node of the current node. After the search 413 is complete, successful or not, the head of the list is reset. 414 415 This means the current handler and every handler that has already been 416 checked are not on the list while a handler is run. If a resumption is thrown 417 during the handling of another resumption the active handlers and all the 418 other handler checked up to this point will not be checked again. 419 420 This structure also supports new handler added while the resumption is being 421 handled. These are added to the front of the list, pointing back along the 422 stack -- the first one will point over all the checked handlers -- and the 423 ordering is maintained. 408 424 409 425 \subsection{Libunwind Compatibility} … … 438 454 439 455 Cancellation also uses libunwind to do its stack traversal and unwinding, 440 however it uses a different primary function \codeC{_Unwind_ForcedUnwind}.456 however it uses a different primary function @_Unwind_ForcedUnwind@. 441 457 Details of its interface can be found in the unwind section. 442 458 -
doc/theses/andrew_beach_MMath/unwinding.tex
rb6a8b31 rd95969a 1 \chapter{ Unwinding in \CFA}1 \chapter{\texorpdfstring{Unwinding in \CFA}{Unwinding in Cforall}} 2 2 3 3 Stack unwinding is the process of removing things from the stack. Within … … 10 10 Even this is fairly simple if nothing needs to happen when the stack unwinds. 11 11 Traditional C can unwind the stack by saving and restoring state (with 12 \codeC{setjmp} \& \codeC{longjmp}). However many languages define actions that12 @setjmp@ \& @longjmp@). However many languages define actions that 13 13 have to be taken when something is removed from the stack, such as running 14 a variable's destructor or a \codeCFA{try} statement's \codeCFA{finally}14 a variable's destructor or a @try@ statement's @finally@ 15 15 clause. Handling this requires walking the stack going through each stack 16 16 frame. … … 29 29 30 30 \CFA uses two primary functions in libunwind to create most of its 31 exceptional control-flow: \codeC{_Unwind_RaiseException}and32 \codeC{_Unwind_ForcedUnwind}.31 exceptional control-flow: @_Unwind_RaiseException@ and 32 @_Unwind_ForcedUnwind@. 33 33 Their operation is divided into two phases: search and clean-up. The search 34 34 phase -- phase 1 -- is used to scan the stack but not unwinding it. The … … 44 44 A personality function performs three tasks, although not all have to be 45 45 present. The tasks performed are decided by the actions provided. 46 \codeC{_Unwind_Action}is a bitmask of possible actions and an argument of46 @_Unwind_Action@ is a bitmask of possible actions and an argument of 47 47 this type is passed into the personality function. 48 48 \begin{itemize} 49 \item \codeC{_UA_SEARCH_PHASE}is passed in search phase and tells the49 \item@_UA_SEARCH_PHASE@ is passed in search phase and tells the 50 50 personality function to check for handlers. If there is a handler in this 51 51 stack frame, as defined by the language, the personality function should 52 return \codeC{_URC_HANDLER_FOUND}. Otherwise it should return53 \codeC{_URC_CONTINUE_UNWIND}.54 \item \codeC{_UA_CLEANUP_PHASE}is passed in during the clean-up phase and52 return @_URC_HANDLER_FOUND@. Otherwise it should return 53 @_URC_CONTINUE_UNWIND@. 54 \item@_UA_CLEANUP_PHASE@ is passed in during the clean-up phase and 55 55 means part or all of the stack frame is removed. The personality function 56 56 should do whatever clean-up the language defines 57 57 (such as running destructors/finalizers) and then generally returns 58 \codeC{_URC_CONTINUE_UNWIND}.59 \item \codeC{_UA_HANDLER_FRAME}means the personality function must install58 @_URC_CONTINUE_UNWIND@. 59 \item@_UA_HANDLER_FRAME@ means the personality function must install 60 60 a handler. It is also passed in during the clean-up phase and is in addition 61 61 to the clean-up action. libunwind provides several helpers for the personality 62 62 function here. Once it is done, the personality function must return 63 \codeC{_URC_INSTALL_CONTEXT}.63 @_URC_INSTALL_CONTEXT@. 64 64 \end{itemize} 65 65 The personality function is given a number of other arguments. Some are for 66 compatability and there is the \codeC{struct _Unwind_Context}pointer which66 compatability and there is the @struct _Unwind_Context@ pointer which 67 67 passed to many helpers to get information about the current stack frame. 68 68 … … 72 72 raise-exception but with some extras. 73 73 The first it passes in an extra action to the personality function on each 74 stack frame, \codeC{_UA_FORCE_UNWIND}, which means a handler cannot be74 stack frame, @_UA_FORCE_UNWIND@, which means a handler cannot be 75 75 installed. 76 76 … … 83 83 stack frames have been removed. By the standard API this is marked by setting 84 84 the stack pointer inside the context passed to the stop function. However both 85 GCC and Clang add an extra action for this case \codeC{_UA_END_OF_STACK}.85 GCC and Clang add an extra action for this case @_UA_END_OF_STACK@. 86 86 87 87 Each time function the stop function is called it can do one or two things. 88 When it is not the end of the stack it can return \codeC{_URC_NO_REASON}to88 When it is not the end of the stack it can return @_URC_NO_REASON@ to 89 89 continue unwinding. 90 90 % Is there a reason that NO_REASON is used instead of CONTINUE_UNWIND? … … 93 93 are provided to do it. 94 94 95 \section{\ CFA Implementation}95 \section{\texorpdfstring{\CFA Implementation}{Cforall Implementation}} 96 96 97 97 To use libunwind, \CFA provides several wrappers, its own storage, … … 113 113 114 114 The stop function is very simple. It checks the end of stack flag to see if 115 it is finished unwinding. If so, it calls \codeC{exit}to end the process,115 it is finished unwinding. If so, it calls @exit@ to end the process, 116 116 otherwise it returns with no-reason to continue unwinding. 117 117 % Yeah, this is going to have to change. … … 128 128 location of the instruction pointer and stack layout, which varies with 129 129 compiler and optimization levels. So for frames where there are only 130 destructors, GCC's attribute cleanup with the \texttt{-fexception}flag is130 destructors, GCC's attribute cleanup with the @-fexception@ flag is 131 131 sufficient to handle unwinding. 132 132 133 133 The only functions that require more than that are those that contain 134 \codeCFA{try} statements. A \codeCFA{try} statement has a \codeCFA{try} 135 clause, some number of \codeCFA{catch} clauses and \codeCFA{catchResume}136 clauses and may have a \codeCFA{finally} clause. Of these only \codeCFA{try}137 statements with \codeCFA{catch}clauses need to be transformed and only they138 and the \codeCFA{try}clause are involved.134 @try@ statements. A @try@ statement has a @try@ 135 clause, some number of @catch@ clauses and @catchResume@ 136 clauses and may have a @finally@ clause. Of these only @try@ 137 statements with @catch@ clauses need to be transformed and only they 138 and the @try@ clause are involved. 139 139 140 The \codeCFA{try}statement is converted into a series of closures which can140 The @try@ statement is converted into a series of closures which can 141 141 access other parts of the function according to scoping rules but can be 142 passed around. The \codeCFA{try}clause is converted into the try functions,143 almost entirely unchanged. The \codeCFA{catch}clauses are converted into two142 passed around. The @try@ clause is converted into the try functions, 143 almost entirely unchanged. The @catch@ clauses are converted into two 144 144 functions; the match function and the catch function. 145 145 … … 153 153 runs the handler's body. 154 154 155 These three functions are passed to \codeC{try_terminate}. This is an155 These three functions are passed to @try_terminate@. This is an 156 156 % Maybe I shouldn't quote that, it isn't its actual name. 157 157 internal hand-written function that has its own personality function and … … 167 167 handler was found in this frame. If it was then the personality function 168 168 installs the handler, which is setting the instruction pointer in 169 \codeC{try_terminate}to an otherwise unused section that calls the catch169 @try_terminate@ to an otherwise unused section that calls the catch 170 170 function, passing it the current exception and handler index. 171 \codeC{try_terminate}returns as soon as the catch function returns.171 @try_terminate@ returns as soon as the catch function returns. 172 172 173 173 At this point control has returned to normal control flow. -
doc/theses/fangren_yu_COOP_F20/Report.tex
rb6a8b31 rd95969a 17 17 \usepackage[usenames]{color} 18 18 \input{common} % common CFA document macros 19 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true, pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}19 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 20 20 \usepackage{breakurl} 21 21 \urlstyle{sf} … … 76 76 \renewcommand{\subsectionmark}[1]{\markboth{\thesubsection\quad #1}{\thesubsection\quad #1}} 77 77 \pagenumbering{roman} 78 \linenumbers % comment out to turn off line numbering78 %\linenumbers % comment out to turn off line numbering 79 79 80 80 \maketitle 81 81 \pdfbookmark[1]{Contents}{section} 82 \tableofcontents 83 84 \clearpage 82 85 83 \thispagestyle{plain} 86 84 \pagenumbering{arabic} 87 85 88 86 \begin{abstract} 87 \CFA is an evolutionary, non-object-oriented extension of the C programming language, featuring a parametric type-system, and is currently under active development. The reference compiler for the \CFA language, @cfa-cc@, has some of its major components dated back to the early 2000s, which are based on inefficient data structures and algorithms. This report introduces improvements targeting the expression resolution algorithm, suggested by a recent prototype experiment on a simplified model, which are implemented in @cfa-cc@ to support the full \CFA language. These optimizations speed up the compiler by a factor of 20 across the existing \CFA codebase, bringing the compilation time of a mid-sized \CFA source file down to the 10-second level. A few problem cases derived from realistic code examples are analyzed in detail, with proposed solutions. This work is a critical step in the \CFA project development to achieve its eventual goal of being used alongside C for large software systems. 89 88 \end{abstract} 90 89 90 \clearpage 91 \section*{Acknowledgements} 92 \begin{sloppypar} 93 I would like to thank everyone in the \CFA team for their contribution towards this project. Programming language design and development is a tough subject and requires a lot of teamwork. Without the collaborative efforts from the team, this project could not have been a success. Specifically, I would like to thank Andrew Beach for introducing me to the \CFA codebase, Thierry Delisle for maintaining the test and build automation framework, Michael Brooks for providing example programs of various experimental language and type system features, and most importantly, Professor Martin Karsten for recommending me to the \CFA team, and my supervisor, Professor Peter Buhr for encouraging me to explore deeply into intricate compiler algorithms. Finally, I gratefully acknowledge the help from Aaron Moss, former graduate from the team and the author of the precedent thesis work, to participate in the \CFA team's virtual conferences and email correspondence, and provide many critical arguments and suggestions. 2020 had been an unusually challenging year for everyone and we managed to keep a steady pace. 94 \end{sloppypar} 95 96 \clearpage 97 \tableofcontents 98 99 \clearpage 91 100 \section{Introduction} 92 101 93 \section{Completed work} 102 \CFA language, developed by the Programming Language Group at the University of Waterloo, has a long history, with the initial language design in 1992 by Glen Ditchfield~\cite{Ditchfield92} and the first proof-of-concept compiler built in 2003 by Richard Bilson~\cite{Bilson03}. Many new features have been added to the language over time, but the core of \CFA's type-system --- parametric functions introduced by the @forall@ clause (hence the name of the language) providing parametric overloading --- remains mostly unchanged. 103 104 The current \CFA reference compiler, @cfa-cc@, is designed using the visitor pattern~\cite{vistorpattern} over an abstract syntax tree (AST), where multiple passes over the AST modify it for subsequent passes. @cfa-cc@ still includes many parts taken directly from the original Bilson implementation, which served as the starting point for this enhancement work to the type system. Unfortunately, the prior implementation did not provide the efficiency required for the language to be practical: a \CFA source file of approximately 1000 lines of code can take a multiple minutes to compile. The cause of the problem is that the old compiler used inefficient data structures and algorithms for expression resolution, which involved significant copying and redundant work. 105 106 This report presents a series of optimizations to the performance-critical parts of the resolver, with a major rework of the compiler data-structures using a functional-programming approach to reduce memory complexity. The improvements were suggested by running the compiler builds with a performance profiler against the \CFA standard-library source-code and a test suite to find the most underperforming components in the compiler algorithm. 107 108 The \CFA team endorses a pragmatic philosophy that focuses on practical implications of language design and implementation rather than theoretical limits. In particular, the compiler is designed to be expressive with respect to code reuse while maintaining type safety, but compromise theoretical soundness in extreme corner cases. However, when these corner cases do appear in actual usage, they need to be thoroughly investigated. A case-by-case analysis is presented for several of these corner cases, some of which point to certain weaknesses in the language design with solutions proposed based on experimental results. 109 110 \section{AST restructuring} 94 111 95 112 \subsection{Memory model with sharing} 96 113 97 A major rework of the abstract syntax tree (AST) data structure in the compiler is completed as the first step of the project. The majority of work were documented in the reference manual of the compiler~\cite{cfa-cc}. To summarize: 98 \begin{itemize} 99 \item 100 AST nodes (and therefore subtrees) can be shared without copying when reused. 101 \item 102 Modifications apply the functional programming principle, making copies for local changes without affecting the original data shared by other owners. In-place mutations are permitted as a special case when sharing does not happen. The logic is implemented by reference counting. 103 \item 104 Memory allocation and freeing are performed automatically using smart pointers. 105 \end{itemize} 106 The resolver algorithm designed for overload resolution naturally introduces a significant amount of reused intermediate representations, especially in the following two places: 107 \begin{itemize} 108 \item 109 Function overload candidates are computed by combining the argument candidates bottom-up, with many of them being a common term. For example, if $n$ overloads of a function @f@ all take an integer for the first parameter but different types for the second (@f( int, int )@, @f( int, double )@, etc.) the first term is reused $n$ times for each of the generated candidate expressions. This effect is particularly bad for deep expression trees. 110 \item 111 In the unification algorithm and candidate elimination step, actual types are obtained by substituting the type parameters by their bindings. Let $n$ be the complexity (\ie number of nodes in representation) of the original type, $m$ be the complexity of bound type for parameters, and $k$ be the number of occurrences of type parameters in the original type. If everything needs to be deep-copied, the substitution step takes $O(n+mk)$ time and memory, while using shared nodes it is reduced to $O(n)$ time and $O(k)$ memory. 112 \end{itemize} 113 One of the worst examples for the old compiler is a long chain of I/O operations 114 \begin{cfa} 115 sout | 1 | 2 | 3 | 4 | ... 116 \end{cfa} 117 The pipe operator is overloaded by \CFA I/O library for every primitive type in C language, as well as I/O manipulators defined by the library. In total there are around 50 overloads for the output stream operation. On resolving the $n$-th pipe operator in the sequence, the first term, which is the result of sub-expression containing $n-1$ pipe operators, is reused to resolve every overload. Therefore at least $O(n^2)$ copies of expression nodes are made during resolution, not even counting type unification cost; combined with two large factors from number of overloads of pipe operators, and that the ``output stream type'' in \CFA is a trait with 27 assertions (which adds to complexity of the pipe operator's type) this makes compiling a long output sequence extremely slow. In new AST representation only $O(n)$ copies are required and type of pipe operator is not copied at all. 118 119 Reduction in space complexity is especially important, as preliminary profiling result on the old compiler build shows that over half of time spent in expression resolution are on memory allocations. 120 114 A major rework of the AST data-structure in the compiler was completed as the first step of the project. The majority of this work is documented in my prior report documenting the compiler reference-manual~\cite{cfa-cc}. To summarize: 115 \begin{itemize} 116 \item 117 AST nodes (and therefore subtrees) can be shared without copying. 118 \item 119 Modifications are performed using functional-programming principles, making copies for local changes without affecting the original data shared by other owners. In-place mutations are permitted as a special case when there is no sharing. The logic is implemented by reference counting. 120 \item 121 Memory allocation and freeing are performed automatically using smart pointers~\cite{smartpointers}. 122 \end{itemize} 123 124 The resolver algorithm, designed for overload resolution, uses a significant amount of reused, and hence copying, for the intermediate representations, especially in the following two places: 125 \begin{itemize} 126 \item 127 Function overload candidates are computed by combining the argument candidates bottom-up, with many being a common term. For example, if $n$ overloads of a function @f@ all take an integer for the first parameter but different types for the second, \eg @f( int, int )@, @f( int, double )@, etc., the first term is copied $n$ times for each of the generated candidate expressions. This copying is particularly bad for deep expression trees. 128 \item 129 In the unification algorithm and candidate elimination step, actual types are obtained by substituting the type parameters by their bindings. Let $n$ be the complexity (\ie number of nodes in representation) of the original type, $m$ be the complexity of the bound type for parameters, and $k$ be the number of occurrences of type parameters in the original type. If every substitution needs to be deep-copied, these copy step takes $O(n+mk)$ time and memory, while using shared nodes it is reduced to $O(n)$ time and $O(k)$ memory. 130 \end{itemize} 131 One of the worst examples for the old compiler is a long chain of I/O operations: 132 \begin{cfa} 133 sout | 1 | 2 | 3 | 4 | ...; // print integer constants 134 \end{cfa} 135 The pipe operator is overloaded by the \CFA I/O library for every primitive type in the C language, as well as I/O manipulators defined by the library. In total, there are around 50 overloads for the output stream operation. On resolving the $n$-th pipe operator in the sequence, the first term, which is the result of sub-expression containing $n-1$ pipe operators, is reused to resolve every overload. Therefore at least $O(n^2)$ copies of expression nodes are made during resolution, not even counting type unification cost; combined with the two large factors from number of overloads of pipe operators, and that the ``output stream type'' in \CFA is a trait with 27 assertions (which adds to complexity of the pipe operator's type) this makes compiling a long output sequence extremely slow. In the new AST representation, only $O(n)$ copies are required and the type of the pipe operator is not copied at all. 136 Reduction in space complexity is especially important, as preliminary profiling results on the old compiler build showed over half of the time spent in expression resolution is on memory allocations. 137 138 Since the compiler codebase is large and the new memory model mostly benefits expression resolution, some of the old data structures are still kept, and a conversion pass happens before and after the general resolve phase. Rewriting every compiler module will take longer, and whether the new model is correct was unknown when this project started, therefore only the resolver is currently implemented with the new data structure. 139 121 140 122 141 \subsection{Merged resolver calls} 123 142 124 The pre-resolve phase of compilation, ina dequately called ``validate'' in the compiler source code, does more than just simple syntax validation, as it also normalizes input program. Some of them, however, requires type information on expressions and therefore needsto call the resolver before the general resolve phase. There are three notable places where the resolver is invoked:125 \begin{itemize} 126 \item 127 Attempt to generate default constructor, copy constructor and destructor for user-defined @struct@ types 128 \item 129 Resolve @with@ statements (the same as in P ython, which introduces fields of a structure directly in scope)143 The pre-resolve phase of compilation, inappropriately called ``validate'' in the compiler source code, has a number of passes that do more than simple syntax and semantic validation; some passes also normalizes the input program. A few of these passes require type information for expressions, and therefore, need to call the resolver before the general resolve phase. There are three notable places where the resolver is invoked: 144 \begin{itemize} 145 \item 146 Generate default constructor, copy constructor and destructor for user-defined @struct@ types. 147 \item 148 Resolve @with@ statements (the same as in Pascal~\cite{pascal}), which introduces fields of a structure directly into a scope. 130 149 \item 131 150 Resolve @typeof@ expressions (cf. @decltype@ in \CC); note that this step may depend on symbols introduced by @with@ statements. 132 151 \end{itemize} 133 Since the compiler codebase is large and the new memory model mostly only benefits expression resolution, the old data structure is still kept, and a conversion pass happens before and after resolve phase. Rewriting every compiler module will take a long time, and whether the new model is correct is still unknown when started, therefore only the resolver is implemented with the new data structure. 134 135 Since the constructor calls were one of the most expensive to resolve (reason will be shown in the next section), pre-resolve phase were taking more time after resolver moves to the more efficient new implementation. To better facilitate the new resolver, every step that requires type information are reintegrated as part of resolver. 136 137 A by-product of this work is that the reversed dependence of @with@ statement and @typeof@ can now be handled. Previously, the compiler is unable to handle cases such as 152 153 Since the constructor calls are one of the most expensive to resolve (reason given in~\VRef{s:SpecialFunctionLookup}), this pre-resolve phase was taking a large amount of time even after the resolver was changed to the more efficient new implementation. The problem is that multiple resolutions repeat a significant amount of work. Therefore, to better facilitate the new resolver, every step that requires type information should be integrated as part of the general resolver phase. 154 155 A by-product of this work is that reversed dependence between @with@ statement and @typeof@ can now be handled. Previously, the compiler was unable to handle cases such as: 138 156 \begin{cfa} 139 157 struct S { int x; }; 140 158 S foo(); 141 159 typeof( foo() ) s; // type is S 142 with (s) { 160 with (s) { 143 161 x; // refers to s.x 144 162 } 145 163 \end{cfa} 146 since t ype of @s@ is still unresolved when handling @with@ expressions. Instead, the new (and correct) approach is to evaluate @typeof@ expressions when the declaration is first seen, and it suffices because of the declaration-before-use rule.164 since the type of @s@ is unresolved when handling @with@ expressions because the @with@ pass follows the @typeof@ pass (interchanging passes only interchanges the problem). Instead, the new (and correct) approach is to evaluate @typeof@ expressions when the declaration is first seen during resolution, and it suffices because of the declaration-before-use rule. 147 165 148 166 149 167 \subsection{Special function lookup} 150 151 Reducing the number of functions looked up for overload resolution is an effective way to gain performance when there are many overloads but most of them are trivially wrong. In practice, most functions have few (if any) overloads but there are notable exceptions. Most importantly, constructor @?{}@, destructor @^?{}@, and assignment @?=?@ are generated for every user-defined type, and in a large source file there can be hundreds of them. Furthermore, many calls to them are generated for initializing variables and passing arguments. This fact makes them the most overloaded and most called functions. 152 153 In an object-oriented programming language, object has methods declared with their types, so a call such as @obj.f()@ only needs to perform lookup in the method table corresponding to type of @obj@. \CFA on the other hand, does not have methods, and all types are open (\ie new operations can be defined on them), so a similar approach will not work in general. However, the ``big 3'' operators have a unique property enforced by the language rules, such that the first parameter must have a reference type. Since \CFA does not have class inheritance, reference type must always match exactly. Therefore, argument-dependent lookup can be implemented for these operators, by using a dedicated symbol table. 154 155 The lookup key used for the special functions is the mangled type name of the first parameter, which acts as the @this@ parameter in an object-oriented language. To handle generic types, the type parameters are stripped off, and only the base type is matched. Note that a constructor (destructor, assignment operator) taking arbitrary @this@ argument, for example @forall( dtype T ) void ?{}( T & );@ is not allowed, and it guarantees that if the @this@ type is known, all possible overloads can be found by searching with the given type. In case that the @this@ argument itself is overloaded, it is resolved first and all possible result types are used for lookup. 156 157 Note that for the generated expressions, the particular variable for @this@ argument is fully known, without overloads, so the majority of constructor call resolutions only need to check for one given object type. Explicit constructor calls and assignment statements sometimes may require lookup for multiple types. In the extremely rare case that type of @this@ argument is yet unbound, everything will have to be checked, just like without the argument-dependent lookup algorithm; fortunately, this case almost never happens in practice. An example is found in the library function @new@: 168 \label{s:SpecialFunctionLookup} 169 170 Reducing the number of function looked ups for overload resolution is an effective way to gain performance when there are many overloads but most of them are trivially wrong. In practice, most functions have few (if any) overloads but there are notable exceptions. Most importantly, constructor @?{}@, destructor @^?{}@, and assignment @?=?@ are generated for every user-defined type (@struct@ and @union@ in C), and in a large source file there can be hundreds of them. Furthermore, many calls are generated for initializing variables, passing arguments and copying values. This fact makes them the most overloaded and most called functions. 171 172 In an object-oriented programming language, the object-method types are scoped within a class, so a call such as @obj.f()@ only needs to perform lookup in the method table corresponding to the type of @obj@. \CFA on the other hand, does not have methods, and all types are open, \ie new operations can be defined on them without inheritance; at best a \CFA type can be constrained by a translation unit. However, the ``big 3'' operators have a unique property enforced by the language rules: the first parameter must be a reference to its associated type, which acts as the @this@ parameter in an object-oriented language. Since \CFA does not have class inheritance, the reference type must always match exactly. Therefore, argument-dependent lookup can be implemented for these operators by using a dedicated, fast symbol-table. 173 174 The lookup key for the special functions is the mangled type name of the first parameter. To handle generic types, the type parameters are stripped off, and only the base type is matched. Note a constructor (destructor, assignment operator) may not take an arbitrary @this@ argument, \eg @forall( dtype T ) void ?{}( T & )@, thus guaranteeing that if the @this@ type is known, all possible overloads can be found by searching with this given type. In the case where the @this@ argument itself is overloaded, it is resolved first and all possible result types are used for lookup. 175 176 Note that for a generated expression, the particular variable for the @this@ argument is fully known, without overloads, so the majority of constructor-call resolutions only need to check for one given object type. Explicit constructor calls and assignment statements sometimes require lookup for multiple types. In the extremely rare case that the @this@-argument type is unbound, all necessary types are guaranteed to be checked, as for the previous lookup without the argument-dependent lookup; fortunately, this complex case almost never happens in practice. An example is found in the library function @new@: 158 177 \begin{cfa} 159 178 forall( dtype T | sized( T ), ttype TT | { void ?{}( T &, TT ); } ) 160 179 T * new( TT p ) { return &(*malloc()){ p }; } 161 180 \end{cfa} 162 as @malloc@ may return a pointer to any type, depending on context. 163 164 Interestingly, this particular line of code actually caused another complicated issue, where the unusually massive work of checking every constructor in presence makes the case even worse. Section~\ref{s:TtypeResolutionInfiniteRecursion} presents a detailed analysis for theproblem.165 166 The ``callable'' operator @?()@ (cf. @operator()@ in \CC) c ould also be included in the special operator list, as it is usually only on user-defined types, and the restriction that first argument must be a reference seems reasonable in this case.181 as @malloc@ may return a pointer to any type, depending on context. 182 183 Interestingly, this particular declaration actually causes another complicated issue, making the complex checking of every constructor even worse. \VRef[Section]{s:TtypeResolutionInfiniteRecursion} presents a detailed analysis of this problem. 184 185 The ``callable'' operator @?()@ (cf. @operator()@ in \CC) can also be included in this special operator list, as it is usually only on user-defined types, and the restriction that the first argument must be a reference seems reasonable in this case. 167 186 168 187 169 188 \subsection{Improvement of function type representation} 170 189 171 Since substituting type parameters with their bound types is one fundamental operation in many parts of resolver algorithm (particularly unification and environment binding), making as few copies of type nodes as possible helps reducing memory complexity. Even with the new memory management model, allocation is still a significant factor of resolver performance. Conceptually, operations on type nodes of AST should be performed in functional programming style, treating the data structure as immutable and only copy when necessary. The in-place mutation is a mere optimization that does not change logic of operations. 172 The model was broken on function types by an inappropriate design. Function types require some special treatment due to the existence of assertions. In particular, it must be able to distinguish two different kinds of type parameter usage: 190 Since substituting type parameters with their bound types is one fundamental operation in many parts of resolver algorithm (particularly unification and environment binding), making as few copies of type nodes as possible helps reducing memory complexity. Even with the new memory management model, allocation is still a significant factor of resolver performance. Conceptually, operations on type nodes of the AST should be performed in functional-programming style, treating the data structure as immutable and only copying when necessary. The in-place mutation is a mere optimization that does not change the logic for operations. 191 192 However, the model was broken for function types by an inappropriate design. Function types require special treatment due to the existence of assertions that constrain the types it supports. Specifically, it must be possible to distinguish two different kinds of type parameter usage: 173 193 \begin{cfa} 174 194 forall( dtype T ) void foo( T * t ) { 175 forall( dtype U ) void bar( T * t, U* u ) { ... }176 } 177 \end{cfa} 178 Here, only @U@ is a free parameter in declaration of @bar@, as it appears in the function's own forall clause; while @T@ is not free.179 180 Moreover, the resolution algorithm also has to distinguish type bindings of multiple calls to the same function, for example with195 forall( dtype U ) void bar( @T@ * t, @U@ * u ) { ... } 196 } 197 \end{cfa} 198 Here, only @U@ is a free parameter in the nested declaration of function @bar@, as @T@ must be bound at the call site when resolving @bar@. 199 200 Moreover, the resolution algorithm also has to distinguish type bindings of multiple calls to the same function, \eg: 181 201 \begin{cfa} 182 202 forall( dtype T ) int foo( T x ); 183 foo( foo( 1.0 ) );184 \end{cfa} 185 The inner call has binding (T: double) while the outer call has binding (T: int). Therefore a unique representation of free parameters in each expression is required. This was previously done by creating a copy of the parameter declarations inside function type, and fixing references afterwards. However, fixing references is an inherently deep operation that does not work well with functional programming model, as it must be evaluated eagerlyon the entire syntax tree representing the function type.186 187 The revised approach generates a unique ID value for each function call expression instance and represents an occurrence of free parameter type with a pair of generated ID and the original parameter declaration, so that references do not need to be fixed, and a shallow copy of function type is possible.188 189 Note that after the change, all declaration nodes in syntax tree representation maps one-to-one with the actual declarations in the program, and therefore are guaranteed to be unique. Such property can potentially enable more optimizations, and some related ideas are presented after Section~\ref{s:SharedSub-ExpressionCaseUniqueExpressions}.203 int i = foo( foo( 1.0 ) ); 204 \end{cfa} 205 The inner call has binding (T: double) while the outer call has binding (T: int). Therefore a unique representation for the free parameters is required in each expression. This type binding was previously done by creating a copy of the parameter declarations inside the function type and fixing references afterwards. However, fixing references is an inherently deep operation that does not work well with the functional-programming style, as it forces eager evaluation on the entire syntax tree representing the function type. 206 207 The revised approach generates a unique ID value for each function call expression instance and represents an occurrence of a free-parameter type with a pair of generated ID and original parameter declaration, so references are unique and a shallow copy of the function type is possible. 208 209 Note that after the change, all declaration nodes in the syntax-tree representation now map one-to-one with the actual declarations in the program, and therefore are guaranteed to be unique. This property can potentially enable more optimizations, and some related ideas are presented at the end of \VRef{s:SharedSub-ExpressionCaseUniqueExpressions}. 190 210 191 211 192 212 \subsection{Improvement of pruning steps} 193 213 194 A minor improvement for candidate elimination is to skip the step on the function overloads themselves and only perform on results of function application. As function calls are usually by name, the name resolution rule dictates that every function candidate necessarily has a different type; indirect function calls are rare, and when they do appear, they usually will not have many possible interpretations, and those rarely matches exactly in argument type. Since function types have a much more complex representation than data types (with multiple parameters and assertions), checking equality on them also takes longer.195 196 A brief test of this approach shows that the number of function overloads considered in expression resolution increases by a negligible amount of less than 1 percent, while type comparisons in candidate elimination are cut by more than half. Improvement is consistent over all \CFA source files in the test suite.214 A minor improvement for candidate elimination is to skip the step on the function overloads and only check the results of function application. As function calls are usually by name (versus pointers to functions), the name resolution rule dictates that every function candidate necessarily has a different type; indirect function calls are rare, and when they do appear, there are even fewer cases with multiple interpretations, and these rarely match exactly in argument type. Since function types have a much more complex representation (with multiple parameters and assertions) than data types, checking equality on them also takes longer. 215 216 A brief test of this approach shows that the number of function overloads considered in expression resolution increases by an amount of less than 1 percent, while type comparisons in candidate elimination are reduced by more than half. This improvement is consistent over all \CFA source files in the test suite. 197 217 198 218 … … 200 220 \label{s:SharedSub-ExpressionCaseUniqueExpressions} 201 221 202 Unique expression denotes an expression that must be evaluated only once, to prevent unwanted side effects. It is currently only a compiler artifact, generated on tuple member expression of the form222 Unique expression denotes an expression evaluated only once to prevent unwanted side effects. It is currently only a compiler artifact, generated for tuple-member expression of the form: 203 223 \begin{cfa} 204 224 struct S { int a; int b; }; … … 206 226 s.[a, b]; // tuple member expression, type is [int, int] 207 227 \end{cfa} 208 If the aggregate expression contains function calls, it cannot be evaluated multiple times:228 If the aggregate expression is function call, it cannot be evaluated multiple times: 209 229 \begin{cfa} 210 230 S makeS(); 211 makeS().[a, b]; // this should only make one S231 makeS().[a, b]; // this should only generate a unique S 212 232 \end{cfa} 213 233 Before code generation, the above expression is internally represented as … … 226 246 \end{cfa} 227 247 at code generation, where @_unique_var@ and @_unique_var_evaluated@ are generated variables whose scope covers all appearances of the same expression. 228 229 Note that although the unique expression is only used for tuple expansion now, it is a generally useful construction, and can be seen in other languages, such as Scala's @lazy val@~\cite{Scala}; therefore it could be worthwhile to introduce the unique expression to a broader context in \CFA and even make it directly available to programmers. 230 231 In the compiler's visitor pattern, however, this creates a problem where multiple paths to a logically unique expression exist, so it may be modified more than once and become ill-formed; some specific intervention is required to ensure that unique expressions are only visited once. Furthermore, a unique expression appearing in more than one places will be copied on mutation so its representation is no longer unique. Some hacks are required to keep it in sync, and the methods are different when mutating the unique expression instance itself or its underlying expression. 232 233 Example when mutating the underlying expression (visit-once guard) 248 The conditional check ensures a single call to @makeS()@ even though there are logically multiple calls because of the tuple field expansion. 249 250 Note that although the unique expression is only used for tuple expansion now, it is a generally useful construction, and is seen in other programming languages, such as Scala's @lazy val@~\cite{Scala}; therefore it may be worthwhile to introduce the unique expression to a broader context in \CFA and even make it directly available to programmers. 251 252 In the compiler's visitor pattern, however, this creates a problem where multiple paths to a logically unique expression exist, so it may be modified more than once and become ill-formed; some specific intervention is required to ensure unique expressions are only visited once. Furthermore, a unique expression appearing in more than one places is copied on mutation so its representation is no longer unique. 253 254 Currently, special cases are required to keep everything synchronized, and the methods are different when mutating the unique expression instance itself or its underlying expression: 255 \begin{itemize} 256 \item 257 When mutating the underlying expression (visit-once guard) 234 258 \begin{cfa} 235 259 void InsertImplicitCalls::previsit( const ast::UniqueExpr * unqExpr ) { 236 if ( visitedIds.count( unqExpr->id ) ) visit_children = false;260 @if ( visitedIds.count( unqExpr->id ) ) visit_children = false;@ 237 261 else visitedIds.insert( unqExpr->id ); 238 262 } 239 263 \end{cfa} 240 Example when mutating the unique instance itself, which actually creates copies 264 \item 265 When mutating the unique instance itself, which actually creates copies 241 266 \begin{cfa} 242 267 auto mutExpr = mutate( unqExpr ); // internally calls copy when shared 243 if ( ! unqMap.count( unqExpr->id ) ) { 268 @if ( ! unqMap.count( unqExpr->id ) ) {@ 244 269 ... 245 270 } else { … … 248 273 } 249 274 \end{cfa} 250 Such workaround seems difficult to be fit into a common visitor template. This suggests the memory model may need different kinds of nodes to accurately represent the syntax tree. 251 252 Together with the fact that declaration nodes are always unique, it is possible that AST nodes can be classified by three different types: 253 \begin{itemize} 254 \item 255 \textbf{Strictly unique} with only one owner (declarations); 256 \item 257 \textbf{Logically unique} with (possibly) many owners but should not be copied (unique expression example presented here); 258 \item 259 \textbf{Shared} by functional programming model, which assume immutable data structure and are copied on mutation. 275 \end{itemize} 276 Such workarounds are difficult to fit into the common visitor pattern, which suggests the memory model may need different kinds of nodes to accurately represent this feature in the AST. 277 278 Given that declaration nodes are unique, it is possible for AST nodes to be divided into three different types: 279 \begin{itemize} 280 \item 281 \textbf{Singleton} with only one owner (declarations); 282 \item 283 \textbf{No-copy} with multiple owners but cannot be copied (unique expression example presented here); 284 \item 285 \textbf{Copy} by functional-programming style, which assumes immutable data structures that are copied on mutation. 260 286 \end{itemize} 261 287 The boilerplate code can potentially handle these three cases differently. … … 264 290 \section{Analysis of resolver algorithm complexity} 265 291 266 The focus of this chapter is to identify and analyze some realistic cases that cause resolver algorithm to have an exponential run time. As previous work has shown [3], the overload resolution problem in \CFA has worst-case exponential complexity; however, only few specific patterns can trigger the exponential complexity in practice. Implementing heuristic-based optimization for those selected cases is helpful to alleviate the problem.292 The focus of this section is to identify and analyze some realistic cases that cause the resolver algorithm to have an exponential runtime. As previous work has shown~\cite[\S~4.2.1]{Moss19}, the overload resolution problem in \CFA has worst-case exponential complexity; however, only few specific patterns can trigger the exponential complexity in practice. Implementing heuristic-based optimization for those selected cases is helpful to alleviate the problem. 267 293 268 294 … … 270 296 \label{s:UnboundReturnType} 271 297 272 The interaction of return type overloading and polymorphic functions creates this problem of function calls with unbound returntype, and is further complicated by the presence of assertions.298 The interaction of return-type overloading and polymorphic functions creates function calls with unbounded return-type, and is further complicated by the presence of assertions. 273 299 The prime example of a function with unbound return type is the type-safe version of C @malloc@: 274 300 \begin{cfa} 275 // size deduced from type, so no need to provide the size argument 276 forall( dtype T | sized( T ) ) T * malloc( void ); 277 \end{cfa} 278 Unbound return type can be problematic in resolver algorithm complexity because a single match of function call with unbound return type may create multiple candidates. In the worst case, consider a function declared to return any @otype@: 301 forall( dtype T | sized( T ) ) 302 T * malloc( void ) { return (T *)malloc( sizeof(T) ); } // call C malloc 303 int * i = malloc(); // type deduced from left-hand size $\Rightarrow$ no size argument or return cast 304 \end{cfa} 305 An unbound return-type is problematic in resolver complexity because a single match of a function call with an unbound return type may create multiple candidates. In the worst case, consider a function declared that returns any @otype@ (defined \VPageref{otype}): 279 306 \begin{cfa} 280 307 forall( otype T ) T anyObj( void ); 281 308 \end{cfa} 282 As the resolver attempts to satisfy the otype constraint on @T@, a single call to @anyObj()@ without the result type known creates at least as many candidates as the number of complete types currently in scope; with generic types it becomes even worse, for example, assuming a declaration of generic pairis available at that point:309 As the resolver attempts to satisfy the otype constraint on @T@, a call to @anyObj()@ in an expression, without the result type known, creates at least as many candidates as the number of complete types currently in scope; with generic types it becomes even worse, \eg assuming a declaration of a generic @pair@ is available at that point: 283 310 \begin{cfa} 284 311 forall( otype T, otype U ) struct pair { T first; U second; }; 285 312 \end{cfa} 286 Then an @anyObj()@ call can result in arbitrarily complex types, such as @pair( pair( int, int ), pair( int,int ) )@, and the depth can grow indefinitely until the specified parameter depth limit, thus creating exponentially many candidates. However, the expected types allowed by parent expressions are practically very few, so most of those interpretations are invalid; if the result type is never bound up to top level, by the semantic rules it is ambiguous if there are more than one valid bindings, and resolution can fail fast. It is therefore reasonable to delay resolving assertions on an unbound parameter in return type; however, with the current cost model, such behavior may further cause irregularities in candidate selection, such that the presence of assertions can change the preferred candidate, even when order of expression costs are supposed to stay the same. Detailed analysis of this issue will be presented later, in the correctness part.313 Then an @anyObj()@ call can result in arbitrarily complex types, such as @pair( pair( int, int ), pair( int, int ) )@, and the depth can grow indefinitely until a specified parameter-depth limit, thus creating exponentially many candidates. However, the expected types allowed by parent expressions are practically very few, so most of those interpretations are invalid; if the result type is never bound up to the top level, by the semantic rules it is ambiguous if there is more than one valid binding and resolution fails quickly. It is therefore reasonable to delay resolving assertions on an unbound parameter in a return type; however, with the current cost model, such behavior may further cause irregularities in candidate selection, such that the presence of assertions can change the preferred candidate, even when order of expression costs are supposed to stay the same. A detailed analysis of this issue is presented in \VRef{s:AnalysisTypeSystemCorrectness}. 287 314 288 315 … … 290 317 \label{s:TtypeResolutionInfiniteRecursion} 291 318 292 @ttype@ (``tuple type'') is a relatively new addition to the language that attempts to provide type-safe variadic argument semantics. Unlike regular @dtype@ parameters, @ttype@ is only valid in function parameter list, and may only appear once as the type of last parameter. At the call site, a @ttype@ parameter is bound to the tuple type of all remaining functioncall arguments.319 @ttype@ (``tuple type'') is a relatively new addition to the language that attempts to provide type-safe variadic argument semantics. Unlike regular @dtype@ parameters, @ttype@ is only valid in a function parameter-list, and may only appear once as the last parameter type. At the call site, a @ttype@ parameter is bound to the tuple type of all remaining function-call arguments. 293 320 294 321 There are two kinds of idiomatic @ttype@ usage: one is to provide flexible argument forwarding, similar to the variadic template in \CC (\lstinline[language=C++]|template<typename... args>|), as shown below in the implementation of @unique_ptr@ … … 298 325 T * data; 299 326 }; 300 forall( dtype T | sized( T ), ttype Args| { void ?{}( T &, Args ); })301 void ?{}( unique_ptr( T ) & this, Args args) {302 this.data = new( args );303 } 304 \end{cfa} 305 the other is to implement structural recursion in the first-rest manner:306 \begin{cfa} 307 forall( otype T, ttype Params| { void process( T ); void func( Params ); })327 forall( dtype T | sized( T ), @ttype Args@ | { void ?{}( T &, Args ); }) 328 void ?{}( unique_ptr( T ) & this, Args @args@ ) { 329 this.data = new( @args@ ); // forward constructor arguments to dynamic allocator 330 } 331 \end{cfa} 332 The other usage is to implement structural recursion in the first-rest pattern: 333 \begin{cfa} 334 forall( otype T, @ttype Params@ | { void process( T ); void func( Params ); }) 308 335 void func( T arg1, Params p ) { 309 336 process( arg1 ); 310 func( p ); 311 } 312 \end{cfa} 313 For the second use case, it is important that the number of parameters in the recursive call go down, since the call site must deduce all assertion candidates, and that is only possible if by just looking at argument types (and not their values), the recursion is known to be completed in a finite number of steps. 314 315 In recent experiments, however, some flaw in the type binding rules can lead to the first kind of @ttype@ use case produce an invalid candidate that the resolver enters an infinite loop. 316 317 This bug was discovered in an attempt to raise assertion recursive depth limit and one of the library program takes exponentially longer time to compile. The cause of the problem is identified to be the following set of functions. 318 File @memory.cfa@ contains 319 \begin{cfa} 320 #include "memory.hfa" 321 #include "stdlib.hfa" 322 \end{cfa} 323 where file @memory.hfa@ contains the @unique_ptr@ declaration above, and two other similar functions with @ttype@ parameter: 324 \begin{cfa} 325 forall( dtype T | sized( T ), ttype Args | { void ?{}( T &, Args ); }) { 337 func( @p@ ); // recursive call until base case of one argument 338 } 339 \end{cfa} 340 For the second use case, it is imperative the number of parameters in the recursive call goes down, since the call site must deduce all assertion candidates, and that is only possible if by observation of the argument types (and not their values), the recursion is known to be completed in a finite number of steps. 341 342 In recent experiments, however, a flaw in the type-binding rules can lead to the first kind of @ttype@ use case producing an invalid candidate and the resolver enters an infinite loop. 343 This bug was discovered in an attempt to raise the assertion recursive-depth limit and one of the library programs took exponentially longer to compile. The cause of the problem is the following set of functions: 344 \begin{cfa} 345 // unique_ptr declaration from above 346 347 forall( dtype T | sized( T ), ttype Args | { void ?{}( T &, Args ); } ) { // distribute forall clause 326 348 void ?{}( counter_data( T ) & this, Args args ); 327 349 void ?{}( counter_ptr( T ) & this, Args args ); 328 350 void ?{}( unique_ptr( T ) & this, Args args ); 329 351 } 330 \end{cfa} 331 File @stdlib.hfa@ contains 332 \begin{cfa} 352 333 353 forall( dtype T | sized( T ), ttype TT | { void ?{}( T &, TT ); } ) 334 T * new( TT p ) { return &(*malloc()){ p }; } 335 \end{cfa} 336 337 In the expression @(*malloc()){p}@, the type of object being constructed is yet unknown, since the return type information is not immediately provided. That caused every constructor to be searched, and while normally a bound @ttype@ cannot be unified with any free parameter, it is possible with another free @ttype@. Therefore in addition to the correct option provided by assertion, 3 wrong options are examined, each of which again requires the same assertion, for an unknown base type T and @ttype@ arguments, and that becomes an infinite loop, until the specified recursion limit and resolution is forced to fail. Moreover, during the recursion steps, number of candidates grows exponentially, since there are always 3 options at each step. 338 339 Unfortunately, @ttype@ to @ttype@ binding is necessary, to allow calling the function provided by assertion indirectly. 340 \begin{cfa} 341 forall( dtype T | sized( T ), ttype Args | { void ?{}( T &, Args ); }) 342 void ?{}( unique_ptr( T ) & this, Args args ) { this.data = (T * )new( args ); } 343 \end{cfa} 344 Here the constructor assertion is used for the @new( args )@ call. 354 T * new( TT p ) { return @&(*malloc()){ p };@ } 355 \end{cfa} 356 In the expression @(*malloc()){p}@, the type of the object being constructed is unknown, since the return-type information is not immediately available. That causes every constructor to be searched, and while normally a bound @ttype@ cannot be unified with any free parameter, it is possible with another free @ttype@. Therefore, in addition to the correct option provided by the assertion, 3 wrong options are examined, each of which again requires the same assertion, for an unknown base-type @T@ and @ttype@ argument, which becomes an infinite loop until the specified recursion limit and resolution is fails. Moreover, during the recursion steps, the number of candidates grows exponentially, since there are always 3 options at each step. 357 358 Unfortunately, @ttype@ to @ttype@ binding is necessary, to allow indirectly calling a function provided in an assertion. 359 \begin{cfa} 360 forall( dtype T | sized( T ), ttype Args | { @void ?{}( T &, Args );@ }) 361 void ?{}( unique_ptr( T ) & this, Args args ) { this.data = (T *)@new( args )@; } // constructor call 362 \end{cfa} 363 Here the constructor assertion is used by the @new( args )@ call to indirectly call the constructor on the allocated storage. 345 364 Therefore, it is hard, perhaps impossible, to solve this problem by tweaking the type binding rules. An assertion caching algorithm can help improve this case by detecting cycles in recursion. 346 365 347 Meanwhile, without the caching algorithm implemented, some changes in the \CFA source code are enough to eliminate this problem, at least in the current codebase. Note that the issue only happens with an overloaded variadic function, which rarely appears in practice, since the idiomatic use cases are for argument forwarding and self-recursion. The only overloaded @ttype@ function so far discovered in all of \CFA standard library code is the constructor, and by utilizing the argument-dependent lookup process described in Section~\ref{s:UnboundReturnType}, adding a cast before constructor call gets rid of the issue.348 \begin{cfa} 349 T * new( TT p ) { return &(* (T * )malloc()){ p }; }366 Meanwhile, without a caching algorithm implemented, some changes in the \CFA source code are enough to eliminate this problem, at least in the current codebase. Note that the issue only happens with an overloaded variadic function, which rarely appears in practice, since the idiomatic use cases are for argument forwarding and self-recursion. The only overloaded @ttype@ function so far discovered in all of \CFA standard library is the constructor, and by utilizing the argument-dependent lookup process described in \VRef{s:UnboundReturnType}, adding a cast before the constructor call removes the issue. 367 \begin{cfa} 368 T * new( TT p ) { return &(*@(T * )@malloc()){ p }; } 350 369 \end{cfa} 351 370 … … 353 372 \subsection{Reused assertions in nested generic type} 354 373 355 The following test of deeply nested dynamic generic type reveals that locally caching reused assertions is necessary, rather than just a resolver optimization, because recomputing assertions can result in bloated generated code size:374 The following test of deeply nested, dynamic generic type reveals that locally caching reused assertions is necessary, rather than just a resolver optimization, because recomputing assertions can result in bloated generated code size: 356 375 \begin{cfa} 357 376 struct nil {}; … … 361 380 int main() { 362 381 #if N==0 363 nil x;382 nil @x@; 364 383 #elif N==1 365 cons( size_t, nil ) x;384 cons( size_t, nil ) @x@; 366 385 #elif N==2 367 cons( size_t, cons( size_t, nil ) ) x;386 cons( size_t, cons( size_t, nil ) ) @x@; 368 387 #elif N==3 369 cons( size_t, cons( size_t, cons( size_t, nil ) ) ) x;388 cons( size_t, cons( size_t, cons( size_t, nil ) ) ) @x@; 370 389 // similarly for N=4,5,6 371 390 #endif 372 391 } 373 392 \end{cfa} 374 At the declaration of @x@, it is implicitly initialized by generated constructor call, w hose signature is given by393 At the declaration of @x@, it is implicitly initialized by generated constructor call, with signature: 375 394 \begin{cfa} 376 395 forall( otype L, otype R ) void ?{}( cons( L, R ) & ); 377 396 \end{cfa} 378 Note that the @otype@ constraint contains 4 assertions: 397 where the @otype@ constraint contains the 4 assertions:\label{otype} 379 398 \begin{cfa} 380 399 void ?{}( L & ); // default constructor … … 383 402 L & ?=?( L &, L & ); // assignment 384 403 \end{cfa} 385 Now since the right hand side of outermost cons is again a cons, recursive assertions are required. When the compiler cannot cache and reuse already resolved assertions, it becomes a problem, as each of those 4 pending assertions again asks for 4 more assertions one level below. Without any caching, number of resolved assertions grows exponentially, while that is obviously unnecessary since there are only $n+1$ different types involved. Even worse, this causes exponentially many wrapper functions generated later at the codegen step, and results in huge compiled binary. 386 387 \ begin{table}[h]404 405 \begin{table}[htb] 406 \centering 388 407 \caption{Compilation results of nested cons test} 408 \label{t:NestedConsTest} 389 409 \begin{tabular}{|r|r|r|} 390 410 \hline … … 402 422 \end{table} 403 423 404 As the local functions are implemented by emitting executable code on the stack~\cite{gcc-nested-func}, it eventually means that compiled code also has exponential run time. This problem has evident practical implications, as nested collection types are frequently used in real production code. 405 424 Now since the right hand side of outermost cons is again a cons, recursive assertions are required. \VRef[Table]{t:NestedConsTest} shows when the compiler does not cache and reuse already resolved assertions, it becomes a problem, as each of these 4 pending assertions again asks for 4 more assertions one level below. Without caching, the number of resolved assertions grows exponentially, which is unnecessary since there are only $n+1$ different types involved. Even worse, this problem causes exponentially many wrapper functions to be generated at the backend, resulting in a huge binary. As the local functions are implemented by emitting executable code on the stack~\cite{gcc-nested-func}, it means that compiled code also has exponential run time. This problem has practical implications, as nested collection types are frequently used in real production code. 406 425 407 426 \section{Analysis of type system correctness} 427 \label{s:AnalysisTypeSystemCorrectness} 408 428 409 429 In Moss' thesis~\cite[\S~4.1.2,~p.~45]{Moss19}, the author presents the following example: … … 422 442 From the set of candidates whose parameter and argument types have been unified and whose assertions have been satisfied, those whose sub-expression interpretations have the smallest total cost of conversion are selected ... The total cost of conversion for each of these candidates is then calculated based on the implicit conversions and polymorphism involved in adapting the types of the sub-expression interpretations to the formal parameter types. 423 443 \end{quote} 424 With this model, the algorithm picks @g1@ in resolving the @f( g( 42 ) )@ call, which seems to be undesirable. 425 426 There are further evidence that shows the Bilson model is fundamentally incorrect, following the discussion of unbound return type in Section~\ref{s:UnboundReturnType}. By the conversion cost specification, a binding from a polymorphic type parameter to a concrete type incurs a polymorphic cost of 1. It remains unspecified \emph{when} the type parameters should become bound. When the parameterized types appear in the function parameters, they can be deduced from the argument type, and there is no ambiguity. In the unbound return case, however, the binding may happen at any stage in expression resolution, therefore it is impossible to define a unique local conversion cost. Note that type binding happens exactly once per parameter in resolving the entire expression, so the global binding cost is unambiguously 1. 427 428 As per the current compiler implementation, it does have a notable inconsistency in handling such case. For any unbound parameter that does \emph{not} come with an associated assertion, it remains unbound to the parent expression; for those that does however, they are immediately bound in the assertion resolution step, and concrete result types are used in the parent expressions. 429 444 With this model, the algorithm picks @g1@ in resolving the @f( g( 42 ) )@ call, which is undesirable. 445 446 There is further evidence that shows the Bilson model is fundamentally incorrect, following the discussion of unbound return type in \VRef{s:UnboundReturnType}. By the conversion-cost specification, a binding from a polymorphic type-parameter to a concrete type incurs a polymorphic cost of 1. It remains unspecified \emph{when} the type parameters should become bound. When the parameterized types appear in function parameters, they can be deduced from the argument type, and there is no ambiguity. In the unbound return case, however, the binding may happen at any stage in expression resolution, therefore it is impossible to define a unique local conversion cost. Note that type binding happens exactly once per parameter in resolving the entire expression, so the global binding cost is unambiguously 1. 447 448 In the current compiler implementation, there is a notable inconsistency in handling this case. For any unbound parameter that does \emph{not} come with an associated assertion, it remains unbound to the parent expression; for those that do, however, they are immediately bound in the assertion resolution step, and concrete result types are used in the parent expressions. 430 449 Consider the following example: 431 450 \begin{cfa} … … 433 452 void h( int * ); 434 453 \end{cfa} 435 The expression @h( f() )@ eventually has a total cost of 1 from binding (T: int), but in the eager resolution model, the cost of 1 may occur either atcall to @f@ or at call to @h@, and with the assertion resolution triggering a binding, the local cost of @f()@ is (0 poly, 0 spec) with no assertions, but (1 poly, -1 spec) with an assertion:436 \begin{cfa} 437 forall( dtype T | { void g( T * ); }) T * f( void );454 The expression @h( f() )@ eventually has a total cost of 1 from binding (T: int), but in the eager-resolution model, the cost of 1 may occur either at the call to @f@ or at call to @h@, and with the assertion resolution triggering a binding, the local cost of @f()@ is (0 poly, 0 spec) with no assertions, but (1 poly, -1 spec) with an assertion: 455 \begin{cfa} 456 forall( dtype T | @{ void g( T * ); }@ ) T * f( void ); 438 457 void g( int * ); 439 458 void h( int * ); 440 459 \end{cfa} 441 and that contradicts the principle that adding assertions should make expression cost lower. Furthermore, the time at which type binding and assertion resolution happens is an implementation detail of the compiler, but not a part of language definition. That means two compliant \CFA compilers, one performing immediate assertion resolution at each step, and one delaying assertion resolution on unbound types, can produce different expression costs and therefore different candidate selection, making the language rule itself partially undefined and thereforeunsound. By the above reasoning, the updated cost model using global sum of costs should be accepted as the standard. It also allows the compiler to freely choose when to resolve assertions, as the sum of total costs is independent of that choice; more optimizations regarding assertion resolution can also be implemented.460 and that contradicts the principle that adding assertions should make expression cost lower. Furthermore, the time at which type binding and assertion resolution happens is an implementation detail of the compiler, not part of the language definition. That means two compliant \CFA compilers, one performing immediate assertion resolution at each step, and one delaying assertion resolution on unbound types, can produce different expression costs and therefore different candidate selection, making the language rule itself partially undefined, and therefore, unsound. By the above reasoning, the updated cost model using global sum of costs should be accepted as the standard. It also allows the compiler to freely choose when to resolve assertions, as the sum of total costs is independent of that choice; more optimizations regarding assertion resolution can also be implemented. 442 461 443 462 444 463 \section{Timing results} 445 464 446 For the timing results presented here, the \CFA compiler is built with gcc 9.3.0, and tested on a server machine running Ubuntu 20.04, 64GB RAM and 32-core 2.2 GHz CPU, results reported by the time command, and using only 8 cores in parallel such that the time is close to the case with 100% CPU utilization on a single thread. 447 448 On the most recent build, the \CFA standard library (~1.3 MB of source code) compiles in 4 minutes 47 seconds total processor time (single thread equivalent), with the slowest file taking 13 seconds. The test suite (178 test cases, ~2.2MB of source code) completes within 25 minutes total processor time,\footnote{Including a few runtime tests; total time spent in compilation is approximately 21 minutes.} with the slowest file taking 23 seconds. In contrast, the library build on old compiler takes 85 minutes total, 5 minutes for the slowest file. Full test suite takes too long with old compiler build and is therefore not run, but the slowest test cases take approximately 5 minutes. Overall, the most recent build compared to old build in April 2020, before the project started, is consistently faster by a factor of 20. 449 450 Additionally, 6 selected \CFA source files with distinct features from library and test suite are used to test compiler performance after each of the optimizations are implemented. Test files are from the most recent build and run through C preprocessor to eliminate the factor of header file changes. The selected tests are: 451 \begin{itemize} 452 \item 453 @lib/fstream@ (112 KB)\footnote{File sizes are after preprocessing, with no line information (\lstinline|gcc -E -P|).}: implementation of I/O library 465 For the timing results presented here, the \CFA compiler is built with gcc 9.3.0, and tested on a server machine running Ubuntu 20.04, 64GB RAM and 32-core 2.2 GHz CPU. 466 Timing is reported by the @time@ command and an experiment is run using 8 cores, where each core is at 100\% CPU utilization. 467 468 On the most recent build, the \CFA standard library ($\approx$1.3 MB of source code) compiles in 4 minutes 47 seconds total processor time (single thread equivalent), with the slowest file taking 13 seconds. The test suite (178 test cases, $\approx$2.2MB of source code) completes within 25 minutes total processor time, 469 % PAB: I do not understand this footnote. 470 %\footnote{Including a few runtime tests; total time spent in compilation is approximately 21 minutes.} 471 with the slowest file taking 23 seconds. In contrast, the library build with the old compiler takes 85 minutes total, 5 minutes for the slowest file. The full test-suite takes too long with old compiler build and is therefore not run, but the slowest test cases take approximately 5 minutes. Overall, the most recent build compared to an old build is consistently faster by a factor of 20. 472 473 Additionally, 6 selected \CFA source files with distinct features from the library and test suite are used to illustrate the compiler performance change after each of the implemented optimizations. Test files are from the most recent build and run through the C preprocessor to expand header file, perform macro expansions, but no line number information (@gcc -E -P@). 474 \VRef[Table]{t:SelectedFileByCompilerBuild} shows the selected tests: 475 \begin{itemize} 476 \item 477 @lib/fstream@ (112 KB) 454 478 \item 455 479 @lib/mutex@ (166 KB): implementation of concurrency primitive … … 459 483 @lib/stdlib@ (64 KB): type-safe wrapper to @void *@-based C standard library functions 460 484 \item 461 @test/ ISO2@ (55 KB): application of I/O library485 @test/io2@ (55 KB): application of I/O library 462 486 \item 463 487 @test/thread@ (188 KB): application of threading library 464 488 \end{itemize} 465 466 The \CFA compiler builds are picked from git commit history that passed the test suite, and implement the optimizations incrementally: 467 \begin{itemize} 468 \item 469 \#0 is the first working build of new AST data structure 489 versus \CFA compiler builds picked from the git commit history that implement the optimizations incrementally: 490 \begin{itemize} 491 \item 492 old resolver 493 \item 494 \#0 is the first working build of the new AST data structure 470 495 \item 471 496 \#1 implements special symbol table and argument-dependent lookup 472 497 \item 473 \#2 implements late assertion satisfaction 474 \item 475 \#3 implements revised function type representation 476 \item 477 \#4 skips pruning on expressions with function type (most recent build) 478 \end{itemize} 479 The old resolver with no memory sharing and none of the optimizations above is also tested. 480 \begin{table} 498 \#2 implements late assertion-satisfaction 499 \item 500 \#3 implements revised function-type representation 501 \item 502 \#4 skips pruning on expressions for function types (most recent build) 503 \end{itemize} 504 Reading left to right for a test shows the benefit of each optimization on the cost of compilation. 505 506 \begin{table}[htb] 507 \centering 481 508 \caption{Compile time of selected files by compiler build, in seconds} 509 \label{t:SelectedFileByCompilerBuild} 482 510 \begin{tabular}{|l|r|r|r|r|r|r|} 483 511 \hline … … 502 530 \end{table} 503 531 504 505 532 \section{Conclusion} 506 533 507 Over the course of 8 months of active research and development in \CFA type system and compiler algorithm, performance of the reference \CFA compiler, cfa-cc, has been greatly improved, allowing mid-sized \CFA programs to be compiled and built reasonably fast. As there are also ongoing efforts in the team on building a standard library, evaluating the runtime performance, and attempting to incorporate \CFA with existing software written in C, this project is especially meaningful forpractical purposes.508 509 A nalysis conducted in the project were based significantly on heuristics and practical evidence, as the theoretical bounds and average cases for the expression resolution problem differ. This approach was difficult at start to follow, with an unacceptably slow compiler, since running the program through debugger and validation tools (\eg @gdb@, @valgrind@) adds another order of magnitude to run time, which was already in minutes. However, near the end of the project, many significant improvements have already been made and new optimizations can be tested immediately. The positive feedback in development cycle benefits the \CFA team as a whole, more than just for the compiler optimizations.510 511 Some potential issues of the language that may happen frequently in practice have been identified. Due to the time constraint and complex nature of these problems, a handful of them remain unsolved, but some constructive proposals are made. Notably, introducing a local assertion cache in the resolver is a common solution for a few remaining problems, so that should be the focus of work soon.512 513 The \CFA team are planning on a public alpha release of the language as the compiler performance becomes promising, and other parts of the system, such as a standard library, are also being enhanced. Ideally, the remaining problems should be resolved before release, and the solutions will also be integral to drafting a formal specification.534 Over the course of 8 months of active research and development of the \CFA type system and compiler algorithms, performance of the reference \CFA compiler, cfa-cc, has been greatly improved. Now, mid-sized \CFA programs are compiled reasonably fast. Currently, there are ongoing efforts by the \CFA team to augment the standard library and evaluate its runtime performance, and incorporate \CFA with existing software written in C; therefore this project is especially meaningful for these practical purposes. 535 536 Accomplishing this work was difficult. Analysis conducted in the project is based significantly on heuristics and practical evidence, as the theoretical bounds and average cases for the expression resolution problem differ. As well, the slowness of the initial compiler made attempts to understand why and where problems exist extremely difficult because both debugging and validation tools (\eg @gdb@, @valgrind@, @pref@) further slowed down compilation time. However, by the end of the project, I had found and fixed several significant problems and new optimizations are easier to introduce and test. The reduction in the development cycle benefits the \CFA team as a whole. 537 538 Some potential issues of the language, which happen frequently in practice, have been identified. Due to the time constraint and complex nature of these problems, a handful of them remain unsolved, but some constructive proposals are made. Notably, introducing a local assertion cache in the resolver is a reasonable solution for a few remaining problems, so that should be the focus of future work. 539 540 The \CFA team are planning on a public alpha release of the language as the compiler performance, given my recent improvements, is now useable. Other parts of the system, such as the standard library, have made significant gains due to the speed up in the development cycle. Ideally, the remaining problems should be resolved before release, and the solutions will also be integral to drafting a formal specification. 514 541 515 542 \addcontentsline{toc}{section}{\refname} -
doc/theses/fangren_yu_COOP_S20/Report.tex
rb6a8b31 rd95969a 17 17 \usepackage[usenames]{color} 18 18 \input{common} % common CFA document macros 19 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true, pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}19 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 20 20 \usepackage{breakurl} 21 21 \urlstyle{sf} -
driver/cfa.cc
rb6a8b31 rd95969a 10 10 // Created On : Tue Aug 20 13:44:49 2002 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 17 14:27:28 202013 // Update Count : 44 012 // Last Modified On : Sat Jan 16 07:30:19 2021 13 // Update Count : 442 14 14 // 15 15 … … 499 499 args[nargs++] = "-no-integrated-cpp"; 500 500 args[nargs++] = "-Wno-deprecated"; 501 args[nargs++] = "-Wno-strict-aliasing"; // casting from one type to another 501 502 #ifdef HAVE_CAST_FUNCTION_TYPE 502 503 args[nargs++] = "-Wno-cast-function-type"; -
libcfa/prelude/builtins.c
rb6a8b31 rd95969a 18 18 // type that wraps a pointer and a destructor-like function - used in generating implicit destructor calls for struct members in user-defined functions 19 19 // Note: needs to occur early, because it is used to generate destructor calls during code generation 20 forall( dtype T)20 forall(T &) 21 21 struct __Destructor { 22 22 T * object; … … 25 25 26 26 // defined destructor in the case that non-generated code wants to use __Destructor 27 forall( dtype T)27 forall(T &) 28 28 static inline void ^?{}(__Destructor(T) & x) { 29 29 if (x.object && x.dtor) { … … 34 34 // easy interface into __Destructor's destructor for easy codegen purposes 35 35 extern "C" { 36 forall( dtype T)36 forall(T &) 37 37 static inline void __destroy_Destructor(__Destructor(T) * dtor) { 38 38 ^(*dtor){}; … … 51 51 void abort( const char fmt[], ... ) __attribute__ (( format(printf, 1, 2), __nothrow__, __leaf__, __noreturn__ )); 52 52 53 forall( dtype T)53 forall(T &) 54 54 static inline T & identity(T & i) { 55 55 return i; … … 64 64 static inline void ^?{}($generator &) {} 65 65 66 trait is_generator( dtype T) {66 trait is_generator(T &) { 67 67 void main(T & this); 68 68 $generator * get_generator(T & this); 69 69 }; 70 70 71 forall( dtype T| is_generator(T))71 forall(T & | is_generator(T)) 72 72 static inline T & resume(T & gen) { 73 73 main(gen); … … 78 78 79 79 static inline { 80 forall( dtype DT| { DT & ?+=?( DT &, one_t ); } )80 forall( DT & | { DT & ?+=?( DT &, one_t ); } ) 81 81 DT & ++?( DT & x ) { return x += 1; } 82 82 83 forall( dtype DT| sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?+=?( DT &, one_t ); } )83 forall( DT & | sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?+=?( DT &, one_t ); } ) 84 84 DT & ?++( DT & x ) { DT tmp = x; x += 1; return tmp; } 85 85 86 forall( dtype DT| { DT & ?-=?( DT &, one_t ); } )86 forall( DT & | { DT & ?-=?( DT &, one_t ); } ) 87 87 DT & --?( DT & x ) { return x -= 1; } 88 88 89 forall( dtype DT| sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?-=?( DT &, one_t ); } )89 forall( DT & | sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?-=?( DT &, one_t ); } ) 90 90 DT & ?--( DT & x ) { DT tmp = x; x -= 1; return tmp; } 91 91 92 forall( dtype DT| { int ?!=?( const DT &, zero_t ); } )92 forall( DT & | { int ?!=?( const DT &, zero_t ); } ) 93 93 int !?( const DT & x ) { return !( x != 0 ); } 94 94 } // distribution 95 95 96 96 // universal typed pointer constant 97 static inline forall( dtype DT) DT * intptr( uintptr_t addr ) { return (DT *)addr; }97 static inline forall( DT & ) DT * intptr( uintptr_t addr ) { return (DT *)addr; } 98 98 static inline forall( ftype FT ) FT * intptr( uintptr_t addr ) { return (FT *)addr; } 99 99 … … 156 156 #define __CFA_EXP_OVERFLOW__() 157 157 158 static inline forall( otypeOT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } ) {158 static inline forall( OT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } ) { 159 159 OT ?\?( OT ep, unsigned int y ) { __CFA_EXP__(); } 160 160 OT ?\?( OT ep, unsigned long int y ) { __CFA_EXP__(); } -
libcfa/prelude/prelude-gen.cc
rb6a8b31 rd95969a 159 159 int main() { 160 160 cout << "# 2 \"prelude.cfa\" // needed for error messages from this file" << endl; 161 cout << "trait sized( dtype T) {};" << endl;161 cout << "trait sized(T &) {};" << endl; 162 162 163 163 cout << "//////////////////////////" << endl; … … 264 264 for (auto cvq : qualifiersPair) { 265 265 for (auto is_vol : { " ", "volatile" }) { 266 cout << "forall( dtype DT) void ?{}(" << cvq.first << type << " * " << is_vol << " &, " << cvq.second << "DT *);" << endl;266 cout << "forall(DT &) void ?{}(" << cvq.first << type << " * " << is_vol << " &, " << cvq.second << "DT *);" << endl; 267 267 } 268 268 } … … 279 279 for (auto cvq : qualifiersSingle) { 280 280 for (auto is_vol : { " ", "volatile" }) { 281 cout << "forall( dtype DT) void ?{}(" << cvq << " DT" << " * " << is_vol << " &);" << endl;281 cout << "forall(DT &) void ?{}(" << cvq << " DT" << " * " << is_vol << " &);" << endl; 282 282 } 283 283 for (auto is_vol : { " ", "volatile" }) { 284 cout << "forall( dtype DT) void ^?{}(" << cvq << " DT" << " * " << is_vol << " &);" << endl;284 cout << "forall(DT &) void ^?{}(" << cvq << " DT" << " * " << is_vol << " &);" << endl; 285 285 } 286 286 } … … 290 290 for (auto is_vol : { " ", "volatile" }) { 291 291 for (auto cvq : qualifiersSingle) { 292 cout << "forall( dtype DT) void ?{}( " << cvq << type << " * " << is_vol << " &, zero_t);" << endl;292 cout << "forall(DT &) void ?{}( " << cvq << type << " * " << is_vol << " &, zero_t);" << endl; 293 293 } 294 294 } … … 317 317 for (auto op : pointerOperators) { 318 318 auto forall = [&op]() { 319 cout << "forall( dtype DT" << op.sized << ") ";319 cout << "forall(DT &" << op.sized << ") "; 320 320 }; 321 321 for (auto type : { "DT"/*, "void"*/ } ) { … … 408 408 for (auto is_vol : { " ", "volatile" }) { 409 409 for (auto cvq : qualifiersPair) { 410 cout << "forall( dtype DT) " << cvq.first << "void * ?=?( " << cvq.first << "void * " << is_vol << " &, " << cvq.second << "DT *);" << endl;410 cout << "forall(DT &) " << cvq.first << "void * ?=?( " << cvq.first << "void * " << is_vol << " &, " << cvq.second << "DT *);" << endl; 411 411 } 412 412 for (auto cvq : qualifiersSingle) { 413 cout << "forall( dtype DT) " << cvq << " DT * ?=?( " << cvq << " DT * " << is_vol << " &, zero_t);" << endl;413 cout << "forall(DT &) " << cvq << " DT * ?=?( " << cvq << " DT * " << is_vol << " &, zero_t);" << endl; 414 414 } 415 415 } -
libcfa/prelude/prelude.old.cf
rb6a8b31 rd95969a 23 23 // ------------------------------------------------------------ 24 24 25 trait sized( dtype T) {};25 trait sized(T &) {}; 26 26 27 27 // ------------------------------------------------------------ … … 68 68 long double _Complex ?--( long double _Complex & ), ?--( volatile long double _Complex & ); 69 69 70 forall( dtype T| sized(T) ) T * ?++( T *& );71 forall( dtype T| sized(T) ) const T * ?++( const T *& );72 forall( dtype T| sized(T) ) volatile T * ?++( volatile T *& );73 forall( dtype T| sized(T) ) const volatile T * ?++( const volatile T *& );74 forall( dtype T| sized(T) ) T * ?--( T *& );75 forall( dtype T| sized(T) ) const T * ?--( const T *& );76 forall( dtype T| sized(T) ) volatile T * ?--( volatile T *& );77 forall( dtype T| sized(T) ) const volatile T * ?--( const volatile T *& );78 79 forall( dtype T| sized(T) ) T & ?[?]( T *, ptrdiff_t );80 forall( dtype T| sized(T) ) const T & ?[?]( const T *, ptrdiff_t );81 forall( dtype T| sized(T) ) volatile T & ?[?]( volatile T *, ptrdiff_t );82 forall( dtype T| sized(T) ) const volatile T & ?[?]( const volatile T *, ptrdiff_t );83 forall( dtype T| sized(T) ) T & ?[?]( ptrdiff_t, T * );84 forall( dtype T| sized(T) ) const T & ?[?]( ptrdiff_t, const T * );85 forall( dtype T| sized(T) ) volatile T & ?[?]( ptrdiff_t, volatile T * );86 forall( dtype T| sized(T) ) const volatile T & ?[?]( ptrdiff_t, const volatile T * );70 forall( T & | sized(T) ) T * ?++( T *& ); 71 forall( T & | sized(T) ) const T * ?++( const T *& ); 72 forall( T & | sized(T) ) volatile T * ?++( volatile T *& ); 73 forall( T & | sized(T) ) const volatile T * ?++( const volatile T *& ); 74 forall( T & | sized(T) ) T * ?--( T *& ); 75 forall( T & | sized(T) ) const T * ?--( const T *& ); 76 forall( T & | sized(T) ) volatile T * ?--( volatile T *& ); 77 forall( T & | sized(T) ) const volatile T * ?--( const volatile T *& ); 78 79 forall( T & | sized(T) ) T & ?[?]( T *, ptrdiff_t ); 80 forall( T & | sized(T) ) const T & ?[?]( const T *, ptrdiff_t ); 81 forall( T & | sized(T) ) volatile T & ?[?]( volatile T *, ptrdiff_t ); 82 forall( T & | sized(T) ) const volatile T & ?[?]( const volatile T *, ptrdiff_t ); 83 forall( T & | sized(T) ) T & ?[?]( ptrdiff_t, T * ); 84 forall( T & | sized(T) ) const T & ?[?]( ptrdiff_t, const T * ); 85 forall( T & | sized(T) ) volatile T & ?[?]( ptrdiff_t, volatile T * ); 86 forall( T & | sized(T) ) const volatile T & ?[?]( ptrdiff_t, const volatile T * ); 87 87 88 88 // ------------------------------------------------------------ … … 107 107 long double _Complex ++?( long double _Complex & ), --?( long double _Complex & ); 108 108 109 forall( dtype T| sized(T) ) T * ++?( T *& );110 forall( dtype T| sized(T) ) const T * ++?( const T *& );111 forall( dtype T| sized(T) ) volatile T * ++?( volatile T *& );112 forall( dtype T| sized(T) ) const volatile T * ++?( const volatile T *& );113 forall( dtype T| sized(T) ) T * --?( T *& );114 forall( dtype T| sized(T) ) const T * --?( const T *& );115 forall( dtype T| sized(T) ) volatile T * --?( volatile T *& );116 forall( dtype T| sized(T) ) const volatile T * --?( const volatile T *& );117 118 forall( dtype T| sized(T) ) T & *?( T * );119 forall( dtype T| sized(T) ) const T & *?( const T * );120 forall( dtype T| sized(T) ) volatile T & *?( volatile T * );121 forall( dtype T| sized(T) ) const volatile T & *?( const volatile T * );109 forall( T & | sized(T) ) T * ++?( T *& ); 110 forall( T & | sized(T) ) const T * ++?( const T *& ); 111 forall( T & | sized(T) ) volatile T * ++?( volatile T *& ); 112 forall( T & | sized(T) ) const volatile T * ++?( const volatile T *& ); 113 forall( T & | sized(T) ) T * --?( T *& ); 114 forall( T & | sized(T) ) const T * --?( const T *& ); 115 forall( T & | sized(T) ) volatile T * --?( volatile T *& ); 116 forall( T & | sized(T) ) const volatile T * --?( const volatile T *& ); 117 118 forall( T & | sized(T) ) T & *?( T * ); 119 forall( T & | sized(T) ) const T & *?( const T * ); 120 forall( T & | sized(T) ) volatile T & *?( volatile T * ); 121 forall( T & | sized(T) ) const volatile T & *?( const volatile T * ); 122 122 forall( ftype FT ) FT & *?( FT * ); 123 123 … … 142 142 !?( float _Complex ), !?( double _Complex ), !?( long double _Complex ); 143 143 144 forall( dtype DT) int !?( DT * );145 forall( dtype DT) int !?( const DT * );146 forall( dtype DT) int !?( volatile DT * );147 forall( dtype DT) int !?( const volatile DT * );144 forall( DT & ) int !?( DT * ); 145 forall( DT & ) int !?( const DT * ); 146 forall( DT & ) int !?( volatile DT * ); 147 forall( DT & ) int !?( const volatile DT * ); 148 148 forall( ftype FT ) int !?( FT * ); 149 149 … … 191 191 long double _Complex ?+?( long double _Complex, long double _Complex ), ?-?( long double _Complex, long double _Complex ); 192 192 193 forall( dtype T| sized(T) ) T * ?+?( T *, ptrdiff_t );194 forall( dtype T| sized(T) ) T * ?+?( ptrdiff_t, T * );195 forall( dtype T| sized(T) ) const T * ?+?( const T *, ptrdiff_t );196 forall( dtype T| sized(T) ) const T * ?+?( ptrdiff_t, const T * );197 forall( dtype T| sized(T) ) volatile T * ?+?( volatile T *, ptrdiff_t );198 forall( dtype T| sized(T) ) volatile T * ?+?( ptrdiff_t, volatile T * );199 forall( dtype T| sized(T) ) const volatile T * ?+?( const volatile T *, ptrdiff_t );200 forall( dtype T| sized(T) ) const volatile T * ?+?( ptrdiff_t, const volatile T * );201 forall( dtype T| sized(T) ) T * ?-?( T *, ptrdiff_t );202 forall( dtype T| sized(T) ) const T * ?-?( const T *, ptrdiff_t );203 forall( dtype T| sized(T) ) volatile T * ?-?( volatile T *, ptrdiff_t );204 forall( dtype T| sized(T) ) const volatile T * ?-?( const volatile T *, ptrdiff_t );205 forall( dtype T| sized(T) ) ptrdiff_t ?-?( const volatile T *, const volatile T * );193 forall( T & | sized(T) ) T * ?+?( T *, ptrdiff_t ); 194 forall( T & | sized(T) ) T * ?+?( ptrdiff_t, T * ); 195 forall( T & | sized(T) ) const T * ?+?( const T *, ptrdiff_t ); 196 forall( T & | sized(T) ) const T * ?+?( ptrdiff_t, const T * ); 197 forall( T & | sized(T) ) volatile T * ?+?( volatile T *, ptrdiff_t ); 198 forall( T & | sized(T) ) volatile T * ?+?( ptrdiff_t, volatile T * ); 199 forall( T & | sized(T) ) const volatile T * ?+?( const volatile T *, ptrdiff_t ); 200 forall( T & | sized(T) ) const volatile T * ?+?( ptrdiff_t, const volatile T * ); 201 forall( T & | sized(T) ) T * ?-?( T *, ptrdiff_t ); 202 forall( T & | sized(T) ) const T * ?-?( const T *, ptrdiff_t ); 203 forall( T & | sized(T) ) volatile T * ?-?( volatile T *, ptrdiff_t ); 204 forall( T & | sized(T) ) const volatile T * ?-?( const volatile T *, ptrdiff_t ); 205 forall( T & | sized(T) ) ptrdiff_t ?-?( const volatile T *, const volatile T * ); 206 206 207 207 // ------------------------------------------------------------ … … 255 255 ?>?( long double, long double ), ?>=?( long double, long double ); 256 256 257 forall( dtype DT) signed int ?<?( DT *, DT * );258 forall( dtype DT) signed int ?<?( const DT *, const DT * );259 forall( dtype DT) signed int ?<?( volatile DT *, volatile DT * );260 forall( dtype DT) signed int ?<?( const volatile DT *, const volatile DT * );261 262 forall( dtype DT) signed int ?>?( DT *, DT * );263 forall( dtype DT) signed int ?>?( const DT *, const DT * );264 forall( dtype DT) signed int ?>?( volatile DT *, volatile DT * );265 forall( dtype DT) signed int ?>?( const volatile DT *, const volatile DT * );266 267 forall( dtype DT) signed int ?<=?( DT *, DT * );268 forall( dtype DT) signed int ?<=?( const DT *, const DT * );269 forall( dtype DT) signed int ?<=?( volatile DT *, volatile DT * );270 forall( dtype DT) signed int ?<=?( const volatile DT *, const volatile DT * );271 272 forall( dtype DT) signed int ?>=?( DT *, DT * );273 forall( dtype DT) signed int ?>=?( const DT *, const DT * );274 forall( dtype DT) signed int ?>=?( volatile DT *, volatile DT * );275 forall( dtype DT) signed int ?>=?( const volatile DT *, const volatile DT * );257 forall( DT & ) signed int ?<?( DT *, DT * ); 258 forall( DT & ) signed int ?<?( const DT *, const DT * ); 259 forall( DT & ) signed int ?<?( volatile DT *, volatile DT * ); 260 forall( DT & ) signed int ?<?( const volatile DT *, const volatile DT * ); 261 262 forall( DT & ) signed int ?>?( DT *, DT * ); 263 forall( DT & ) signed int ?>?( const DT *, const DT * ); 264 forall( DT & ) signed int ?>?( volatile DT *, volatile DT * ); 265 forall( DT & ) signed int ?>?( const volatile DT *, const volatile DT * ); 266 267 forall( DT & ) signed int ?<=?( DT *, DT * ); 268 forall( DT & ) signed int ?<=?( const DT *, const DT * ); 269 forall( DT & ) signed int ?<=?( volatile DT *, volatile DT * ); 270 forall( DT & ) signed int ?<=?( const volatile DT *, const volatile DT * ); 271 272 forall( DT & ) signed int ?>=?( DT *, DT * ); 273 forall( DT & ) signed int ?>=?( const DT *, const DT * ); 274 forall( DT & ) signed int ?>=?( volatile DT *, volatile DT * ); 275 forall( DT & ) signed int ?>=?( const volatile DT *, const volatile DT * ); 276 276 277 277 // ------------------------------------------------------------ … … 302 302 signed int ?==?( one_t, one_t ), ?!=?( one_t, one_t ); 303 303 304 forall( dtype DT) signed int ?==?( DT *, DT * );305 forall( dtype DT) signed int ?==?( const DT *, const DT * );306 forall( dtype DT) signed int ?==?( volatile DT *, volatile DT * );307 forall( dtype DT) signed int ?==?( const volatile DT *, const volatile DT * );304 forall( DT & ) signed int ?==?( DT *, DT * ); 305 forall( DT & ) signed int ?==?( const DT *, const DT * ); 306 forall( DT & ) signed int ?==?( volatile DT *, volatile DT * ); 307 forall( DT & ) signed int ?==?( const volatile DT *, const volatile DT * ); 308 308 forall( ftype FT ) signed int ?==?( FT *, FT * ); 309 forall( dtype DT) signed int ?!=?( DT *, DT * );310 forall( dtype DT) signed int ?!=?( const DT *, const DT * );311 forall( dtype DT) signed int ?!=?( volatile DT *, volatile DT * );312 forall( dtype DT) signed int ?!=?( const volatile DT *, const volatile DT * );309 forall( DT & ) signed int ?!=?( DT *, DT * ); 310 forall( DT & ) signed int ?!=?( const DT *, const DT * ); 311 forall( DT & ) signed int ?!=?( volatile DT *, volatile DT * ); 312 forall( DT & ) signed int ?!=?( const volatile DT *, const volatile DT * ); 313 313 forall( ftype FT ) signed int ?!=?( FT *, FT * ); 314 314 … … 376 376 377 377 forall( ftype FT ) FT * ?=?( FT *&, FT * ); 378 forall( fty peFT ) FT * ?=?( FT * volatile &, FT * );379 380 forall( dtype DT) DT * ?=?( DT * &, DT * );381 forall( dtype DT) DT * ?=?( DT * volatile &, DT * );382 forall( dtype DT) const DT * ?=?( const DT * &, DT * );383 forall( dtype DT) const DT * ?=?( const DT * volatile &, DT * );384 forall( dtype DT) const DT * ?=?( const DT * &, const DT * );385 forall( dtype DT) const DT * ?=?( const DT * volatile &, const DT * );386 forall( dtype DT) volatile DT * ?=?( volatile DT * &, DT * );387 forall( dtype DT) volatile DT * ?=?( volatile DT * volatile &, DT * );388 forall( dtype DT) volatile DT * ?=?( volatile DT * &, volatile DT * );389 forall( dtype DT) volatile DT * ?=?( volatile DT * volatile &, volatile DT * );390 391 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, DT * );392 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, DT * );393 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, const DT * );394 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, const DT * );395 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, volatile DT * );396 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, volatile DT * );397 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, const volatile DT * );398 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, const volatile DT * );399 400 forall( dtype DT) void * ?=?( void * &, DT * );401 forall( dtype DT) void * ?=?( void * volatile &, DT * );402 forall( dtype DT) const void * ?=?( const void * &, DT * );403 forall( dtype DT) const void * ?=?( const void * volatile &, DT * );404 forall( dtype DT) const void * ?=?( const void * &, const DT * );405 forall( dtype DT) const void * ?=?( const void * volatile &, const DT * );406 forall( dtype DT) volatile void * ?=?( volatile void * &, DT * );407 forall( dtype DT) volatile void * ?=?( volatile void * volatile &, DT * );408 forall( dtype DT) volatile void * ?=?( volatile void * &, volatile DT * );409 forall( dtype DT) volatile void * ?=?( volatile void * volatile &, volatile DT * );410 forall( dtype DT) const volatile void * ?=?( const volatile void * &, DT * );411 forall( dtype DT) const volatile void * ?=?( const volatile void * volatile &, DT * );412 forall( dtype DT) const volatile void * ?=?( const volatile void * &, const DT * );413 forall( dtype DT) const volatile void * ?=?( const volatile void * volatile &, const DT * );414 forall( dtype DT) const volatile void * ?=?( const volatile void * &, volatile DT * );415 forall( dtype DT) const volatile void * ?=?( const volatile void * volatile &, volatile DT * );416 forall( dtype DT) const volatile void * ?=?( const volatile void * &, const volatile DT * );417 forall( dtype DT) const volatile void * ?=?( const volatile void * volatile &, const volatile DT * );378 forall( ftyep FT ) FT * ?=?( FT * volatile &, FT * ); 379 380 forall( DT & ) DT * ?=?( DT * &, DT * ); 381 forall( DT & ) DT * ?=?( DT * volatile &, DT * ); 382 forall( DT & ) const DT * ?=?( const DT * &, DT * ); 383 forall( DT & ) const DT * ?=?( const DT * volatile &, DT * ); 384 forall( DT & ) const DT * ?=?( const DT * &, const DT * ); 385 forall( DT & ) const DT * ?=?( const DT * volatile &, const DT * ); 386 forall( DT & ) volatile DT * ?=?( volatile DT * &, DT * ); 387 forall( DT & ) volatile DT * ?=?( volatile DT * volatile &, DT * ); 388 forall( DT & ) volatile DT * ?=?( volatile DT * &, volatile DT * ); 389 forall( DT & ) volatile DT * ?=?( volatile DT * volatile &, volatile DT * ); 390 391 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, DT * ); 392 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, DT * ); 393 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, const DT * ); 394 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, const DT * ); 395 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, volatile DT * ); 396 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, volatile DT * ); 397 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, const volatile DT * ); 398 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, const volatile DT * ); 399 400 forall( DT & ) void * ?=?( void * &, DT * ); 401 forall( DT & ) void * ?=?( void * volatile &, DT * ); 402 forall( DT & ) const void * ?=?( const void * &, DT * ); 403 forall( DT & ) const void * ?=?( const void * volatile &, DT * ); 404 forall( DT & ) const void * ?=?( const void * &, const DT * ); 405 forall( DT & ) const void * ?=?( const void * volatile &, const DT * ); 406 forall( DT & ) volatile void * ?=?( volatile void * &, DT * ); 407 forall( DT & ) volatile void * ?=?( volatile void * volatile &, DT * ); 408 forall( DT & ) volatile void * ?=?( volatile void * &, volatile DT * ); 409 forall( DT & ) volatile void * ?=?( volatile void * volatile &, volatile DT * ); 410 forall( DT & ) const volatile void * ?=?( const volatile void * &, DT * ); 411 forall( DT & ) const volatile void * ?=?( const volatile void * volatile &, DT * ); 412 forall( DT & ) const volatile void * ?=?( const volatile void * &, const DT * ); 413 forall( DT & ) const volatile void * ?=?( const volatile void * volatile &, const DT * ); 414 forall( DT & ) const volatile void * ?=?( const volatile void * &, volatile DT * ); 415 forall( DT & ) const volatile void * ?=?( const volatile void * volatile &, volatile DT * ); 416 forall( DT & ) const volatile void * ?=?( const volatile void * &, const volatile DT * ); 417 forall( DT & ) const volatile void * ?=?( const volatile void * volatile &, const volatile DT * ); 418 418 419 419 //forall( dtype DT ) DT * ?=?( DT * &, zero_t ); 420 420 //forall( dtype DT ) DT * ?=?( DT * volatile &, zero_t ); 421 forall( dtype DT) const DT * ?=?( const DT * &, zero_t );422 forall( dtype DT) const DT * ?=?( const DT * volatile &, zero_t );421 forall( DT & ) const DT * ?=?( const DT * &, zero_t ); 422 forall( DT & ) const DT * ?=?( const DT * volatile &, zero_t ); 423 423 //forall( dtype DT ) volatile DT * ?=?( volatile DT * &, zero_t ); 424 424 //forall( dtype DT ) volatile DT * ?=?( volatile DT * volatile &, zero_t ); 425 forall( dtype DT) const volatile DT * ?=?( const volatile DT * &, zero_t );426 forall( dtype DT) const volatile DT * ?=?( const volatile DT * volatile &, zero_t );425 forall( DT & ) const volatile DT * ?=?( const volatile DT * &, zero_t ); 426 forall( DT & ) const volatile DT * ?=?( const volatile DT * volatile &, zero_t ); 427 427 428 428 forall( ftype FT ) FT * ?=?( FT * &, zero_t ); 429 429 forall( ftype FT ) FT * ?=?( FT * volatile &, zero_t ); 430 430 431 forall( dtype T| sized(T) ) T * ?+=?( T * &, ptrdiff_t );432 forall( dtype T| sized(T) ) T * ?+=?( T * volatile &, ptrdiff_t );433 forall( dtype T| sized(T) ) const T * ?+=?( const T * &, ptrdiff_t );434 forall( dtype T| sized(T) ) const T * ?+=?( const T * volatile &, ptrdiff_t );435 forall( dtype T| sized(T) ) volatile T * ?+=?( volatile T * &, ptrdiff_t );436 forall( dtype T| sized(T) ) volatile T * ?+=?( volatile T * volatile &, ptrdiff_t );437 forall( dtype T| sized(T) ) const volatile T * ?+=?( const volatile T * &, ptrdiff_t );438 forall( dtype T| sized(T) ) const volatile T * ?+=?( const volatile T * volatile &, ptrdiff_t );439 forall( dtype T| sized(T) ) T * ?-=?( T * &, ptrdiff_t );440 forall( dtype T| sized(T) ) T * ?-=?( T * volatile &, ptrdiff_t );441 forall( dtype T| sized(T) ) const T * ?-=?( const T * &, ptrdiff_t );442 forall( dtype T| sized(T) ) const T * ?-=?( const T * volatile &, ptrdiff_t );443 forall( dtype T| sized(T) ) volatile T * ?-=?( volatile T * &, ptrdiff_t );444 forall( dtype T| sized(T) ) volatile T * ?-=?( volatile T * volatile &, ptrdiff_t );445 forall( dtype T| sized(T) ) const volatile T * ?-=?( const volatile T * &, ptrdiff_t );446 forall( dtype T| sized(T) ) const volatile T * ?-=?( const volatile T * volatile &, ptrdiff_t );431 forall( T & | sized(T) ) T * ?+=?( T * &, ptrdiff_t ); 432 forall( T & | sized(T) ) T * ?+=?( T * volatile &, ptrdiff_t ); 433 forall( T & | sized(T) ) const T * ?+=?( const T * &, ptrdiff_t ); 434 forall( T & | sized(T) ) const T * ?+=?( const T * volatile &, ptrdiff_t ); 435 forall( T & | sized(T) ) volatile T * ?+=?( volatile T * &, ptrdiff_t ); 436 forall( T & | sized(T) ) volatile T * ?+=?( volatile T * volatile &, ptrdiff_t ); 437 forall( T & | sized(T) ) const volatile T * ?+=?( const volatile T * &, ptrdiff_t ); 438 forall( T & | sized(T) ) const volatile T * ?+=?( const volatile T * volatile &, ptrdiff_t ); 439 forall( T & | sized(T) ) T * ?-=?( T * &, ptrdiff_t ); 440 forall( T & | sized(T) ) T * ?-=?( T * volatile &, ptrdiff_t ); 441 forall( T & | sized(T) ) const T * ?-=?( const T * &, ptrdiff_t ); 442 forall( T & | sized(T) ) const T * ?-=?( const T * volatile &, ptrdiff_t ); 443 forall( T & | sized(T) ) volatile T * ?-=?( volatile T * &, ptrdiff_t ); 444 forall( T & | sized(T) ) volatile T * ?-=?( volatile T * volatile &, ptrdiff_t ); 445 forall( T & | sized(T) ) const volatile T * ?-=?( const volatile T * &, ptrdiff_t ); 446 forall( T & | sized(T) ) const volatile T * ?-=?( const volatile T * volatile &, ptrdiff_t ); 447 447 448 448 _Bool ?=?( _Bool &, _Bool ), ?=?( volatile _Bool &, _Bool ); … … 723 723 forall( ftype FT ) void ?{}( FT * volatile &, FT * ); 724 724 725 forall( dtype DT) void ?{}( DT * &, DT * );726 forall( dtype DT) void ?{}( const DT * &, DT * );727 forall( dtype DT) void ?{}( const DT * &, const DT * );728 forall( dtype DT) void ?{}( volatile DT * &, DT * );729 forall( dtype DT) void ?{}( volatile DT * &, volatile DT * );730 forall( dtype DT) void ?{}( const volatile DT * &, DT * );731 forall( dtype DT) void ?{}( const volatile DT * &, const DT * );732 forall( dtype DT) void ?{}( const volatile DT * &, volatile DT * );733 forall( dtype DT) void ?{}( const volatile DT * &, const volatile DT * );734 735 forall( dtype DT) void ?{}( void * &, DT * );736 forall( dtype DT) void ?{}( const void * &, DT * );737 forall( dtype DT) void ?{}( const void * &, const DT * );738 forall( dtype DT) void ?{}( volatile void * &, DT * );739 forall( dtype DT) void ?{}( volatile void * &, volatile DT * );740 forall( dtype DT) void ?{}( const volatile void * &, DT * );741 forall( dtype DT) void ?{}( const volatile void * &, const DT * );742 forall( dtype DT) void ?{}( const volatile void * &, volatile DT * );743 forall( dtype DT) void ?{}( const volatile void * &, const volatile DT * );725 forall( DT & ) void ?{}( DT * &, DT * ); 726 forall( DT & ) void ?{}( const DT * &, DT * ); 727 forall( DT & ) void ?{}( const DT * &, const DT * ); 728 forall( DT & ) void ?{}( volatile DT * &, DT * ); 729 forall( DT & ) void ?{}( volatile DT * &, volatile DT * ); 730 forall( DT & ) void ?{}( const volatile DT * &, DT * ); 731 forall( DT & ) void ?{}( const volatile DT * &, const DT * ); 732 forall( DT & ) void ?{}( const volatile DT * &, volatile DT * ); 733 forall( DT & ) void ?{}( const volatile DT * &, const volatile DT * ); 734 735 forall( DT & ) void ?{}( void * &, DT * ); 736 forall( DT & ) void ?{}( const void * &, DT * ); 737 forall( DT & ) void ?{}( const void * &, const DT * ); 738 forall( DT & ) void ?{}( volatile void * &, DT * ); 739 forall( DT & ) void ?{}( volatile void * &, volatile DT * ); 740 forall( DT & ) void ?{}( const volatile void * &, DT * ); 741 forall( DT & ) void ?{}( const volatile void * &, const DT * ); 742 forall( DT & ) void ?{}( const volatile void * &, volatile DT * ); 743 forall( DT & ) void ?{}( const volatile void * &, const volatile DT * ); 744 744 745 745 //forall( dtype DT ) void ?{}( DT * &, zero_t ); 746 746 //forall( dtype DT ) void ?{}( DT * volatile &, zero_t ); 747 forall( dtype DT) void ?{}( const DT * &, zero_t );747 forall( DT & ) void ?{}( const DT * &, zero_t ); 748 748 //forall( dtype DT ) void ?{}( volatile DT * &, zero_t ); 749 749 //forall( dtype DT ) void ?{}( volatile DT * volatile &, zero_t ); 750 forall( dtype DT) void ?{}( const volatile DT * &, zero_t );750 forall( DT & ) void ?{}( const volatile DT * &, zero_t ); 751 751 752 752 forall( ftype FT ) void ?{}( FT * &, zero_t ); … … 755 755 forall( ftype FT ) void ?{}( FT * & ); 756 756 757 forall( dtype DT) void ?{}( DT * &);758 forall( dtype DT) void ?{}( const DT * &);759 forall( dtype DT) void ?{}( volatile DT * &);760 forall( dtype DT) void ?{}( const volatile DT * &);757 forall( DT & ) void ?{}( DT * &); 758 forall( DT & ) void ?{}( const DT * &); 759 forall( DT & ) void ?{}( volatile DT * &); 760 forall( DT & ) void ?{}( const volatile DT * &); 761 761 762 762 void ?{}( void * &); … … 768 768 forall( ftype FT ) void ^?{}( FT * & ); 769 769 770 forall( dtype DT) void ^?{}( DT * &);771 forall( dtype DT) void ^?{}( const DT * &);772 forall( dtype DT) void ^?{}( volatile DT * &);773 forall( dtype DT) void ^?{}( const volatile DT * &);770 forall( DT & ) void ^?{}( DT * &); 771 forall( DT & ) void ^?{}( const DT * &); 772 forall( DT & ) void ^?{}( volatile DT * &); 773 forall( DT & ) void ^?{}( const volatile DT * &); 774 774 775 775 void ^?{}( void * &); -
libcfa/prelude/sync-builtins.cf
rb6a8b31 rd95969a 206 206 _Bool __sync_bool_compare_and_swap(volatile unsigned __int128 *, unsigned __int128, unsigned __int128,...); 207 207 #endif 208 forall( dtype T) _Bool __sync_bool_compare_and_swap(T * volatile *, T *, T*, ...);208 forall(T &) _Bool __sync_bool_compare_and_swap(T * volatile *, T *, T*, ...); 209 209 210 210 char __sync_val_compare_and_swap(volatile char *, char, char,...); … … 223 223 unsigned __int128 __sync_val_compare_and_swap(volatile unsigned __int128 *, unsigned __int128, unsigned __int128,...); 224 224 #endif 225 forall( dtype T) T * __sync_val_compare_and_swap(T * volatile *, T *, T*,...);225 forall(T &) T * __sync_val_compare_and_swap(T * volatile *, T *, T*,...); 226 226 227 227 char __sync_lock_test_and_set(volatile char *, char,...); … … 326 326 void __atomic_exchange(volatile unsigned __int128 *, volatile unsigned __int128 *, volatile unsigned __int128 *, int); 327 327 #endif 328 forall( dtype T) T * __atomic_exchange_n(T * volatile *, T *, int);329 forall( dtype T) void __atomic_exchange(T * volatile *, T * volatile *, T * volatile *, int);328 forall(T &) T * __atomic_exchange_n(T * volatile *, T *, int); 329 forall(T &) void __atomic_exchange(T * volatile *, T * volatile *, T * volatile *, int); 330 330 331 331 _Bool __atomic_load_n(const volatile _Bool *, int); … … 359 359 void __atomic_load(const volatile unsigned __int128 *, volatile unsigned __int128 *, int); 360 360 #endif 361 forall( dtype T) T * __atomic_load_n(T * const volatile *, int);362 forall( dtype T) void __atomic_load(T * const volatile *, T **, int);361 forall(T &) T * __atomic_load_n(T * const volatile *, int); 362 forall(T &) void __atomic_load(T * const volatile *, T **, int); 363 363 364 364 _Bool __atomic_compare_exchange_n(volatile char *, char *, char, _Bool, int, int); … … 390 390 _Bool __atomic_compare_exchange (volatile unsigned __int128 *, unsigned __int128 *, unsigned __int128 *, _Bool, int, int); 391 391 #endif 392 forall( dtype T) _Bool __atomic_compare_exchange_n (T * volatile *, T **, T*, _Bool, int, int);393 forall( dtype T) _Bool __atomic_compare_exchange (T * volatile *, T **, T**, _Bool, int, int);392 forall(T &) _Bool __atomic_compare_exchange_n (T * volatile *, T **, T*, _Bool, int, int); 393 forall(T &) _Bool __atomic_compare_exchange (T * volatile *, T **, T**, _Bool, int, int); 394 394 395 395 void __atomic_store_n(volatile _Bool *, _Bool, int); … … 423 423 void __atomic_store(volatile unsigned __int128 *, unsigned __int128 *, int); 424 424 #endif 425 forall( dtype T) void __atomic_store_n(T * volatile *, T *, int);426 forall( dtype T) void __atomic_store(T * volatile *, T **, int);425 forall(T &) void __atomic_store_n(T * volatile *, T *, int); 426 forall(T &) void __atomic_store(T * volatile *, T **, int); 427 427 428 428 char __atomic_add_fetch (volatile char *, char, int); -
libcfa/src/Makefile.am
rb6a8b31 rd95969a 76 76 stdlib.hfa \ 77 77 time.hfa \ 78 bits/weakso_locks.hfa \ 78 79 containers/maybe.hfa \ 79 80 containers/pair.hfa \ -
libcfa/src/bitmanip.hfa
rb6a8b31 rd95969a 100 100 unsigned long long int floor2( unsigned long long int n, unsigned long long int align ) { verify( is_pow2( align ) ); return n & -align; } 101 101 102 // forall( otypeT | { T ?&?( T, T ); T -?( T ); } )102 // forall( T | { T ?&?( T, T ); T -?( T ); } ) 103 103 // T floor2( T n, T align ) { verify( is_pow2( align ) ); return n & -align; } 104 104 … … 115 115 unsigned long long int ceiling2( unsigned long long int n, unsigned long long int align ) { verify( is_pow2( align ) ); return -floor2( -n, align ); } 116 116 117 // forall( otypeT | { T floor2( T, T ); T -?( T ); } )117 // forall( T | { T floor2( T, T ); T -?( T ); } ) 118 118 // T ceiling2( T n, T align ) { verify( is_pow2( align ) ); return -floor2( -n, align ); } 119 119 } // distribution -
libcfa/src/bits/algorithm.hfa
rb6a8b31 rd95969a 17 17 18 18 #ifdef SAFE_SORT 19 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort2( T * arr );20 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort3( T * arr );21 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort4( T * arr );22 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort5( T * arr );23 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort6( T * arr );24 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sortN( T * arr, size_t dim );19 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort2( T * arr ); 20 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort3( T * arr ); 21 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort4( T * arr ); 22 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort5( T * arr ); 23 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sort6( T * arr ); 24 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) static inline void __libcfa_small_sortN( T * arr, size_t dim ); 25 25 26 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )26 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 27 27 static inline void __libcfa_small_sort( T * arr, size_t dim ) { 28 28 switch( dim ) { … … 41 41 #define SWAP(x,y) { T a = min(arr[x], arr[y]); T b = max(arr[x], arr[y]); arr[x] = a; arr[y] = b;} 42 42 43 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )43 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 44 44 static inline void __libcfa_small_sort2( T * arr ) { 45 45 SWAP(0, 1); 46 46 } 47 47 48 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )48 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 49 49 static inline void __libcfa_small_sort3( T * arr ) { 50 50 SWAP(1, 2); … … 53 53 } 54 54 55 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )55 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 56 56 static inline void __libcfa_small_sort4( T * arr ) { 57 57 SWAP(0, 1); … … 62 62 } 63 63 64 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )64 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 65 65 static inline void __libcfa_small_sort5( T * arr ) { 66 66 SWAP(0, 1); … … 75 75 } 76 76 77 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )77 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 78 78 static inline void __libcfa_small_sort6( T * arr ) { 79 79 SWAP(1, 2); … … 91 91 } 92 92 93 forall( otypeT | { int ?<?( T, T ); int ?>?( T, T ); } )93 forall( T | { int ?<?( T, T ); int ?>?( T, T ); } ) 94 94 static inline void __libcfa_small_sortN( T * arr, size_t dim ) { 95 95 int i, j; … … 112 112 static inline void __libcfa_small_sortN( void* * arr, size_t dim ); 113 113 114 forall( dtype T)114 forall( T & ) 115 115 static inline void __libcfa_small_sort( T* * arr, size_t dim ) { 116 116 switch( dim ) { -
libcfa/src/bits/collection.hfa
rb6a8b31 rd95969a 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // bits/collection.hfa -- PUBLIC 8 // Intrusive singly-linked list 9 // 10 // Author : Colby Alexander Parsons & Peter A. Buhr 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 16 1 17 #pragma once 2 #include <stdio.h> // REMOVE THIS AFTER DEBUGGING3 4 18 5 19 struct Colable { 6 struct Colable * next;// next node in the list20 // next node in the list 7 21 // invariant: (next != 0) <=> listed() 22 struct Colable * next; 8 23 }; 9 24 #ifdef __cforall … … 31 46 32 47 // // wrappers to make Collection have T 33 // forall( dtype T) {48 // forall( T & ) { 34 49 // T *& Next( T * n ) { 35 50 // return (T *)Next( (Colable *)n ); … … 38 53 } // distribution 39 54 40 forall( dtype T| { T *& Next ( T * ); } ) {55 static inline forall( T & | { T *& Next ( T * ); } ) { 41 56 bool listed( T * n ) { 42 57 return Next( n ) != 0p; … … 53 68 Collection & ?=?( const Collection & ) = void; // no assignment 54 69 55 void ?{}( Collection & collection ) with( collection ) { 70 void ?{}( Collection & collection ) with( collection ) { 56 71 root = 0p; 57 72 } // post: empty() … … 76 91 } // post: elts = null 77 92 78 forall( dtype T) {93 forall( T & ) { 79 94 T * Curr( ColIter & ci ) with( ci ) { 80 95 return (T *)curr; -
libcfa/src/bits/containers.hfa
rb6a8b31 rd95969a 23 23 24 24 #ifdef __cforall 25 forall( dtype T)25 forall(T &) 26 26 #else 27 27 #define T void … … 40 40 41 41 #ifdef __cforall 42 // forall( otypeT | sized(T))42 // forall(T | sized(T)) 43 43 // static inline void ?{}(__small_array(T) & this) {} 44 44 45 forall( dtype T| sized(T))45 forall(T & | sized(T)) 46 46 static inline T & ?[?]( __small_array(T) & this, __lock_size_t idx ) { 47 47 return ((typeof(this.data))this.data)[idx]; 48 48 } 49 49 50 forall( dtype T| sized(T))50 forall(T & | sized(T)) 51 51 static inline T & ?[?]( const __small_array(T) & this, __lock_size_t idx ) { 52 52 return ((typeof(this.data))this.data)[idx]; 53 53 } 54 54 55 forall( dtype T)55 forall(T &) 56 56 static inline T * begin( const __small_array(T) & this ) { 57 57 return ((typeof(this.data))this.data); 58 58 } 59 59 60 forall( dtype T| sized(T))60 forall(T & | sized(T)) 61 61 static inline T * end( const __small_array(T) & this ) { 62 62 return ((typeof(this.data))this.data) + this.size; … … 69 69 70 70 #ifdef __cforall 71 trait is_node( dtype T) {71 trait is_node(T &) { 72 72 T *& get_next( T & ); 73 73 }; … … 78 78 //----------------------------------------------------------------------------- 79 79 #ifdef __cforall 80 forall( dtype TYPE)80 forall(TYPE &) 81 81 #define T TYPE 82 82 #else … … 95 95 96 96 #ifdef __cforall 97 forall( dtype T)97 forall(T &) 98 98 static inline void ?{}( __stack(T) & this ) { 99 99 (this.top){ 0p }; 100 100 } 101 101 102 static inline forall( dtype T| is_node(T) ) {102 static inline forall( T & | is_node(T) ) { 103 103 void push( __stack(T) & this, T * val ) { 104 104 verify( !get_next( *val ) ); … … 126 126 //----------------------------------------------------------------------------- 127 127 #ifdef __cforall 128 forall( dtype TYPE)128 forall(TYPE &) 129 129 #define T TYPE 130 130 #else … … 144 144 145 145 #ifdef __cforall 146 static inline forall( dtype T| is_node(T) ) {146 static inline forall( T & | is_node(T) ) { 147 147 void ?{}( __queue(T) & this ) with( this ) { 148 148 (this.head){ 1p }; … … 215 215 //----------------------------------------------------------------------------- 216 216 #ifdef __cforall 217 forall( dtype TYPE)217 forall(TYPE &) 218 218 #define T TYPE 219 219 #define __getter_t * [T * & next, T * & prev] ( T & ) … … 237 237 238 238 #ifdef __cforall 239 forall( dtype T)239 forall(T & ) 240 240 static inline [void] ?{}( __dllist(T) & this, * [T * & next, T * & prev] ( T & ) __get ) { 241 241 (this.head){ 0p }; … … 245 245 #define next 0 246 246 #define prev 1 247 static inline forall( dtype T) {247 static inline forall(T &) { 248 248 void push_front( __dllist(T) & this, T & node ) with( this ) { 249 249 verify(__get); -
libcfa/src/bits/defs.hfa
rb6a8b31 rd95969a 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // defs.hfa -- 7 // defs.hfa -- Commen macros, functions and typedefs 8 // Most files depend on them and they are always useful to have. 9 // 10 // *** Must not contain code specific to libcfathread *** 8 11 // 9 12 // Author : Thierry Delisle … … 62 65 #endif 63 66 } 67 68 // pause to prevent excess processor bus usage 69 #if defined( __i386 ) || defined( __x86_64 ) 70 #define Pause() __asm__ __volatile__ ( "pause" : : : ) 71 #elif defined( __ARM_ARCH ) 72 #define Pause() __asm__ __volatile__ ( "YIELD" : : : ) 73 #else 74 #error unsupported architecture 75 #endif -
libcfa/src/bits/locks.hfa
rb6a8b31 rd95969a 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // bits/locks.hfa -- Fast internal locks. 7 // bits/locks.hfa -- Basic spinlocks that are reused in the system. 8 // Used for locks that aren't specific to cforall threads and can be used anywhere 9 // 10 // *** Must not contain code specific to libcfathread *** 8 11 // 9 12 // Author : Thierry Delisle … … 19 22 #include "bits/defs.hfa" 20 23 #include <assert.h> 21 22 #ifdef __cforall23 extern "C" {24 #include <pthread.h>25 }26 #endif27 28 // pause to prevent excess processor bus usage29 #if defined( __i386 ) || defined( __x86_64 )30 #define Pause() __asm__ __volatile__ ( "pause" : : : )31 #elif defined( __ARM_ARCH )32 #define Pause() __asm__ __volatile__ ( "YIELD" : : : )33 #else34 #error unsupported architecture35 #endif36 24 37 25 struct __spinlock_t { … … 104 92 enable_interrupts_noPoll(); 105 93 } 106 107 108 #ifdef __CFA_WITH_VERIFY__109 extern bool __cfaabi_dbg_in_kernel();110 #endif111 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 117 struct __bin_sem_t {118 pthread_mutex_t lock;119 pthread_cond_t cond;120 int val;121 };122 123 static inline void ?{}(__bin_sem_t & this) with( this ) {124 // Create the mutex with error checking125 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 required131 val = 0;132 }133 134 static inline void ^?{}(__bin_sem_t & this) with( this ) {135 CHECKED( pthread_mutex_destroy(&lock) );136 CHECKED( pthread_cond_destroy (&cond) );137 }138 139 static inline void wait(__bin_sem_t & this) with( this ) {140 verify(__cfaabi_dbg_in_kernel());141 CHECKED( pthread_mutex_lock(&lock) );142 while(val < 1) {143 pthread_cond_wait(&cond, &lock);144 }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 CHECKED164 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 thread171 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 post221 // Similar to a binary semaphore with a 'one shot' semantic222 // is expected to be discarded after each party call their side223 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 waiting228 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 parked240 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 needed254 // return true if a thread was unparked255 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 upon264 // It is based on the 'oneshot' type to allow multiple futures265 // to block on the same instance, permitting users to block a single266 // thread on "any of" [a given set of] futures.267 // does not support multiple threads waiting on the same future268 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 delete274 // any oneshot : a context has been setup to wait, a thread could wait on it275 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 1p287 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);288 }289 290 // check if the future is available291 bool available( future_t & this ) {292 return this.ptr == 1p;293 }294 295 // Prepare the future to be waited on296 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly297 bool setup( future_t & this, oneshot & wait_ctx ) {298 /* paranoid */ verify( wait_ctx.ptr == 0p );299 // The future needs to set the wait context300 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 context306 /* 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 future314 // When multiple futures are waited for together in "any of" pattern315 // futures that weren't fulfilled before the thread woke up316 // should retract the wait ctx317 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly318 void retract( future_t & this, oneshot & wait_ctx ) {319 // Remove the wait context320 struct oneshot * got = __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);321 322 // got == 0p: future was never actually setup, just return323 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 context327 // 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 consumed331 // the server won't use the pointer again332 // 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 consumed336 // spin until it is safe to move on337 if( got == 2p ) {338 while( this.ptr != 1p ) Pause();339 return;340 }341 342 // got == any thing else, something wen't wrong here, abort343 abort("Future in unexpected state");344 }345 346 // Mark the future as abandoned, meaning it will be deleted by the server347 bool abandon( future_t & this ) {348 /* paranoid */ verify( this.ptr != 3p );349 350 // Mark the future as abandonned351 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST);352 353 // If the future isn't already fulfilled, let the server delete it354 if( got == 0p ) return false;355 356 // got == 2p: the future is ready but the context hasn't fully been consumed357 // spin until it is safe to move on358 if( got == 2p ) {359 while( this.ptr != 1p ) Pause();360 got = 1p;361 }362 363 // The future is completed delete it now364 /* paranoid */ verify( this.ptr != 1p );365 free( &this );366 return true;367 }368 369 // from the server side, mark the future as fulfilled370 // delete it if needed371 bool fulfil( future_t & this ) {372 for() {373 struct oneshot * expected = this.ptr;374 // was this abandoned?375 #if defined(__GNUC__) && __GNUC__ >= 7376 #pragma GCC diagnostic push377 #pragma GCC diagnostic ignored "-Wfree-nonheap-object"378 #endif379 if( expected == 3p ) { free( &this ); return false; }380 #if defined(__GNUC__) && __GNUC__ >= 7381 #pragma GCC diagnostic pop382 #endif383 384 /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen385 /* 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 after388 // If there is no context then we can skip the in progress phase389 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 fulfilled401 bool wait( future_t & this ) {402 oneshot temp;403 if( !setup(this, temp) ) return false;404 405 // Wait context is setup, just wait on it406 bool ret = wait( temp );407 408 // Wait for the future to tru409 while( this.ptr == 2p ) Pause();410 // Make sure the state makes sense411 // Should be fulfilled, could be in progress but it's out of date if so412 // since if that is the case, the oneshot was fulfilled (unparking this thread)413 // and the oneshot should not be needed any more414 __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 consistent418 // with potential calls to avail419 // this.ptr = 1p;420 return ret;421 }422 }423 94 #endif -
libcfa/src/bits/queue.hfa
rb6a8b31 rd95969a 9 9 // instead of being null. 10 10 11 forall( dtype T| { T *& Next ( T * ); } ) {11 forall( T & | { T *& Next ( T * ); } ) { 12 12 struct Queue { 13 13 inline Collection; // Plan 9 inheritance … … 151 151 } // distribution 152 152 153 forall( dtype T| { T *& Next ( T * ); } ) {153 forall( T & | { T *& Next ( T * ); } ) { 154 154 struct QueueIter { 155 155 inline ColIter; // Plan 9 inheritance -
libcfa/src/bits/sequence.hfa
rb6a8b31 rd95969a 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // bits/sequence.hfa -- PUBLIC 8 // Intrusive doubly-linked list 9 // 10 // Author : Colby Alexander Parsons & Peter A. Buhr 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 16 1 17 #pragma once 2 18 … … 6 22 struct Seqable { 7 23 __cfa_anonymous_object(Colable); 8 struct Seqable * back; // pointer to previous node in the list 24 // pointer to previous node in the list 25 struct Seqable * back; 9 26 }; 10 27 … … 27 44 return sq->back; 28 45 } 29 30 // // wrappers to make Collection have T31 // forall( dtype T ) {32 // T *& Back( T * n ) {33 // return (T *)Back( (Seqable *)n );34 // }35 // } // distribution36 46 } // distribution 37 47 … … 43 53 // and the back field of the last node points at the first node (circular). 44 54 45 forall( dtype T | { T *& Back ( T * ); T *& Next ( T * ); }) {55 forall( T & ) { 46 56 struct Sequence { 47 inline Collection; // Plan 9 inheritance 57 // Plan 9 inheritance 58 inline Collection; 48 59 }; 49 60 50 61 static inline { 62 void ?{}( Sequence(T) &, const Sequence(T) & ) = void; // no copy 63 Sequence(T) & ?=?( const Sequence(T) & ) = void; // no assignment 64 65 void ?{}( Sequence(T) & s ) with( s ) { 66 ((Collection &)s){}; 67 } // post: isEmpty() 68 } 69 70 static inline forall(| { T *& Back ( T * ); T *& Next ( T * ); }) { 51 71 // wrappers to make Collection have T 52 72 T & head( Sequence(T) & s ) with( s ) { 53 73 return *(T *)head( (Collection &)s ); 54 74 } // post: empty() & head() == 0 | !empty() & head() in *s 55 56 void ?{}( Sequence(T) &, const Sequence(T) & ) = void; // no copy57 Sequence(T) & ?=?( const Sequence(T) & ) = void; // no assignment58 59 void ?{}( Sequence(T) & s ) with( s ) {60 ((Collection &)s){};61 } // post: isEmpty()62 75 63 76 // Return a pointer to the last sequence element, without removing it. … … 145 158 return n; 146 159 } // post: n->listed() & *n in *s & succ(n) == bef 147 160 148 161 // pre: n->listed() & *n in *s 149 162 T & remove( Sequence(T) & s, T & n ) with( s ) { // O(1) … … 231 244 } // distribution 232 245 233 forall( dtype T| { T *& Back ( T * ); T *& Next ( T * ); } ) {246 forall( T & | { T *& Back ( T * ); T *& Next ( T * ); } ) { 234 247 // SeqIter(T) is used to iterate over a Sequence(T) in head-to-tail order. 235 248 struct SeqIter { … … 285 298 286 299 static inline { 287 void ?{}( SeqIterRev(T) & si ) with( si ) { 300 void ?{}( SeqIterRev(T) & si ) with( si ) { 288 301 ((ColIter &)si){}; 289 302 seq = 0p; … … 291 304 292 305 // Create a iterator active in sequence s. 293 void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 306 void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 294 307 ((ColIter &)si){}; 295 308 seq = &s; … … 297 310 } // post: elts = null 298 311 299 void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) { 312 void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) { 300 313 ((ColIter &)si){}; 301 314 seq = &s; -
libcfa/src/bits/stack.hfa
rb6a8b31 rd95969a 9 9 // instead of being null. 10 10 11 forall( dtype T| { T *& Next ( T * ); } ) {11 forall( T & | { T *& Next ( T * ); } ) { 12 12 struct Stack { 13 13 inline Collection; // Plan 9 inheritance … … 67 67 // order returned by drop(). 68 68 69 forall( dtype T| { T *& Next ( T * ); } ) {69 forall( T & | { T *& Next ( T * ); } ) { 70 70 struct StackIter { 71 71 inline ColIter; // Plan 9 inheritance -
libcfa/src/common.cfa
rb6a8b31 rd95969a 23 23 [ long int, long int ] div( long int num, long int denom ) { ldiv_t qr = ldiv( num, denom ); return [ qr.quot, qr.rem ]; } 24 24 [ long long int, long long int ] div( long long int num, long long int denom ) { lldiv_t qr = lldiv( num, denom ); return [ qr.quot, qr.rem ]; } 25 forall( otypeT | { T ?/?( T, T ); T ?%?( T, T ); } )25 forall( T | { T ?/?( T, T ); T ?%?( T, T ); } ) 26 26 [ T, T ] div( T num, T denom ) { return [ num / denom, num % denom ]; } 27 27 -
libcfa/src/common.hfa
rb6a8b31 rd95969a 21 21 [ long int, long int ] div( long int num, long int denom ); 22 22 [ long long int, long long int ] div( long long int num, long long int denom ); 23 forall( otypeT | { T ?/?( T, T ); T ?%?( T, T ); } )23 forall( T | { T ?/?( T, T ); T ?%?( T, T ); } ) 24 24 [ T, T ] div( T num, T demon ); 25 25 … … 61 61 } // distribution 62 62 63 forall( otypeT | { void ?{}( T &, zero_t ); int ?<?( T, T ); T -?( T ); } )63 forall( T | { void ?{}( T &, zero_t ); int ?<?( T, T ); T -?( T ); } ) 64 64 T abs( T ); 65 65 … … 70 70 intptr_t min( intptr_t t1, intptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization 71 71 uintptr_t min( uintptr_t t1, uintptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization 72 forall( otypeT | { int ?<?( T, T ); } )72 forall( T | { int ?<?( T, T ); } ) 73 73 T min( T t1, T t2 ) { return t1 < t2 ? t1 : t2; } 74 74 … … 76 76 intptr_t max( intptr_t t1, intptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization 77 77 uintptr_t max( uintptr_t t1, uintptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization 78 forall( otypeT | { int ?>?( T, T ); } )78 forall( T | { int ?>?( T, T ); } ) 79 79 T max( T t1, T t2 ) { return t1 > t2 ? t1 : t2; } 80 80 81 forall( otypeT | { T min( T, T ); T max( T, T ); } )81 forall( T | { T min( T, T ); T max( T, T ); } ) 82 82 T clamp( T value, T min_val, T max_val ) { return max( min_val, min( value, max_val ) ); } 83 83 84 forall( otypeT )84 forall( T ) 85 85 void swap( T & v1, T & v2 ) { T temp = v1; v1 = v2; v2 = temp; } 86 86 } // distribution -
libcfa/src/concurrency/coroutine.cfa
rb6a8b31 rd95969a 46 46 47 47 //----------------------------------------------------------------------------- 48 FORALL_DATA_INSTANCE(CoroutineCancelled, ( dtype coroutine_t), (coroutine_t))49 50 forall( dtype T)48 FORALL_DATA_INSTANCE(CoroutineCancelled, (coroutine_t &), (coroutine_t)) 49 50 forall(T &) 51 51 void mark_exception(CoroutineCancelled(T) *) {} 52 52 53 forall( dtype T)53 forall(T &) 54 54 void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src) { 55 55 dst->virtual_table = src->virtual_table; … … 58 58 } 59 59 60 forall( dtype T)60 forall(T &) 61 61 const char * msg(CoroutineCancelled(T) *) { 62 62 return "CoroutineCancelled(...)"; … … 64 64 65 65 // This code should not be inlined. It is the error path on resume. 66 forall( dtype T| is_coroutine(T))66 forall(T & | is_coroutine(T)) 67 67 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ) { 68 68 verify( desc->cancellation ); … … 148 148 // Part of the Public API 149 149 // Not inline since only ever called once per coroutine 150 forall( dtype T| is_coroutine(T))150 forall(T & | is_coroutine(T)) 151 151 void prime(T& cor) { 152 152 $coroutine* this = get_coroutine(cor); -
libcfa/src/concurrency/coroutine.hfa
rb6a8b31 rd95969a 22 22 //----------------------------------------------------------------------------- 23 23 // Exception thrown from resume when a coroutine stack is cancelled. 24 FORALL_DATA_EXCEPTION(CoroutineCancelled, ( dtype coroutine_t), (coroutine_t)) (24 FORALL_DATA_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) ( 25 25 coroutine_t * the_coroutine; 26 26 exception_t * the_exception; 27 27 ); 28 28 29 forall( dtype T)29 forall(T &) 30 30 void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src); 31 31 32 forall( dtype T)32 forall(T &) 33 33 const char * msg(CoroutineCancelled(T) *); 34 34 … … 37 37 // Anything that implements this trait can be resumed. 38 38 // Anything that is resumed is a coroutine. 39 trait is_coroutine( dtype T| IS_RESUMPTION_EXCEPTION(CoroutineCancelled, (T))) {39 trait is_coroutine(T & | IS_RESUMPTION_EXCEPTION(CoroutineCancelled, (T))) { 40 40 void main(T & this); 41 41 $coroutine * get_coroutine(T & this); … … 60 60 //----------------------------------------------------------------------------- 61 61 // Public coroutine API 62 forall( dtype T| is_coroutine(T))62 forall(T & | is_coroutine(T)) 63 63 void prime(T & cor); 64 64 … … 72 72 void __cfactx_invoke_coroutine(void (*main)(void *), void * this); 73 73 74 forall( dtype T)74 forall(T &) 75 75 void __cfactx_start(void (*main)(T &), struct $coroutine * cor, T & this, void (*invoke)(void (*main)(void *), void *)); 76 76 … … 129 129 } 130 130 131 forall( dtype T| is_coroutine(T))131 forall(T & | is_coroutine(T)) 132 132 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ); 133 133 134 134 // Resume implementation inlined for performance 135 forall( dtype T| is_coroutine(T))135 forall(T & | is_coroutine(T)) 136 136 static inline T & resume(T & cor) { 137 137 // optimization : read TLS once and reuse it -
libcfa/src/concurrency/future.hfa
rb6a8b31 rd95969a 19 19 #include "monitor.hfa" 20 20 21 forall( otypeT ) {21 forall( T ) { 22 22 struct future { 23 23 inline future_t; … … 58 58 } 59 59 60 forall( otypeT ) {60 forall( T ) { 61 61 monitor multi_future { 62 62 inline future_t; -
libcfa/src/concurrency/io.cfa
rb6a8b31 rd95969a 41 41 #include "io/types.hfa" 42 42 43 static const char * opcodes[] = {43 __attribute__((unused)) static const char * opcodes[] = { 44 44 "OP_NOP", 45 45 "OP_READV", … … 173 173 __cfadbg_print_safe(io_core, "Kernel I/O : IO_URING enter %d %u %u\n", ring.fd, to_submit, flags); 174 174 ret = syscall( __NR_io_uring_enter, ring.fd, to_submit, 0, flags, (sigset_t *)0p, _NSIG / 8); 175 __cfadbg_print_safe(io_core, "Kernel I/O : IO_URING %d returned %d\n", ring.fd, ret); 176 175 177 if( ret < 0 ) { 176 178 switch((int)errno) { 177 179 case EAGAIN: 178 180 case EINTR: 181 case EBUSY: 179 182 ret = -1; 180 183 break; … … 318 321 319 322 __cfadbg_print_safe(io_core, "Kernel I/O : Fast poller %d (%p) stopping\n", this.ring->fd, &this); 323 324 __ioctx_unregister( this ); 320 325 } 321 326 … … 389 394 390 395 block++; 391 392 abort( "Kernel I/O : all submit queue entries used, yielding\n" );393 396 394 397 yield(); … … 465 468 sqe->flags, 466 469 sqe->ioprio, 467 sqe->off,468 sqe->addr,470 (void*)sqe->off, 471 (void*)sqe->addr, 469 472 sqe->len, 470 473 sqe->accept_flags, … … 491 494 } 492 495 else if( ring.eager_submits ) { 493 __ u32 picked = __submit_to_ready_array( ring, idx, mask );496 __attribute__((unused)) __u32 picked = __submit_to_ready_array( ring, idx, mask ); 494 497 495 498 #if defined(LEADER_LOCK) … … 629 632 sqe->flags, 630 633 sqe->ioprio, 631 sqe->off,632 sqe->addr,634 (void*)sqe->off, 635 (void*)sqe->addr, 633 636 sqe->len, 634 637 sqe->accept_flags, … … 642 645 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 643 646 // Release the consumed SQEs 647 644 648 __release_consumed_submission( ring ); 645 649 // ring.submit_q.sqes[idx].user_data = 3ul64; -
libcfa/src/concurrency/io/setup.cfa
rb6a8b31 rd95969a 42 42 void ^?{}(io_context & this, bool cluster_context) {} 43 43 44 void register_fixed_files( io_context &, int *, unsigned ) {} 45 void register_fixed_files( cluster &, int *, unsigned ) {} 46 44 47 #else 45 48 #include <errno.h> … … 110 113 111 114 static struct { 112 pthread_t thrd; // pthread handle to io poller thread 113 void * stack; // pthread stack for io poller thread 114 int epollfd; // file descriptor to the epoll instance 115 volatile bool run; // Whether or not to continue 115 pthread_t thrd; // pthread handle to io poller thread 116 void * stack; // pthread stack for io poller thread 117 int epollfd; // file descriptor to the epoll instance 118 volatile bool run; // Whether or not to continue 119 volatile bool stopped; // Whether the poller has finished running 120 volatile uint64_t epoch; // Epoch used for memory reclamation 116 121 } iopoll; 117 122 … … 126 131 __cfadbg_print_safe(io_core, "Kernel : Starting io poller thread\n" ); 127 132 128 iopoll.run = true; 129 iopoll.stack = __create_pthread( &iopoll.thrd, iopoll_loop, 0p ); 133 iopoll.stack = __create_pthread( &iopoll.thrd, iopoll_loop, 0p ); 134 iopoll.run = true; 135 iopoll.stopped = false; 136 iopoll.epoch = 0; 130 137 } 131 138 … … 171 178 while( iopoll.run ) { 172 179 __cfadbg_print_safe(io_core, "Kernel I/O - epoll : waiting on io_uring contexts\n"); 180 181 // increment the epoch to notify any deleters we are starting a new cycle 182 __atomic_fetch_add(&iopoll.epoch, 1, __ATOMIC_SEQ_CST); 173 183 174 184 // Wait for events … … 197 207 } 198 208 } 209 210 __atomic_store_n(&iopoll.stopped, true, __ATOMIC_SEQ_CST); 199 211 200 212 __cfadbg_print_safe(io_core, "Kernel : IO poller thread stopping\n" ); … … 493 505 // I/O Context Sleep 494 506 //============================================================================================= 495 #define IOEVENTS EPOLLIN | EPOLLONESHOT496 497 507 static inline void __ioctx_epoll_ctl($io_ctx_thread & ctx, int op, const char * error) { 498 508 struct epoll_event ev; 499 ev.events = IOEVENTS;509 ev.events = EPOLLIN | EPOLLONESHOT; 500 510 ev.data.u64 = (__u64)&ctx; 501 511 int ret = epoll_ctl(iopoll.epollfd, op, ctx.ring->efd, &ev); … … 514 524 } 515 525 526 void __ioctx_unregister($io_ctx_thread & ctx) { 527 // Read the current epoch so we know when to stop 528 size_t curr = __atomic_load_n(&iopoll.epoch, __ATOMIC_SEQ_CST); 529 530 // Remove the fd from the iopoller 531 __ioctx_epoll_ctl(ctx, EPOLL_CTL_DEL, "REMOVE"); 532 533 // Notify the io poller thread of the shutdown 534 iopoll.run = false; 535 sigval val = { 1 }; 536 pthread_sigqueue( iopoll.thrd, SIGUSR1, val ); 537 538 // Make sure all this is done 539 __atomic_thread_fence(__ATOMIC_SEQ_CST); 540 541 // Wait for the next epoch 542 while(curr == iopoll.epoch && !iopoll.stopped) Pause(); 543 } 544 516 545 //============================================================================================= 517 546 // I/O Context Misc Setup … … 520 549 int ret = syscall( __NR_io_uring_register, ctx.thrd.ring->fd, IORING_REGISTER_FILES, files, count ); 521 550 if( ret < 0 ) { 522 abort( "KERNEL ERROR: IO_URING SYSCALL- (%d) %s\n", (int)errno, strerror(errno) );551 abort( "KERNEL ERROR: IO_URING REGISTER - (%d) %s\n", (int)errno, strerror(errno) ); 523 552 } 524 553 -
libcfa/src/concurrency/io/types.hfa
rb6a8b31 rd95969a 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // io/types.hfa -- 7 // io/types.hfa -- PRIVATE 8 // Types used by the I/O subsystem 8 9 // 9 10 // Author : Thierry Delisle … … 21 22 22 23 #include "bits/locks.hfa" 24 #include "kernel/fwd.hfa" 23 25 24 26 #if defined(CFA_HAVE_LINUX_IO_URING_H) … … 133 135 struct $io_ctx_thread; 134 136 void __ioctx_register($io_ctx_thread & ctx); 137 void __ioctx_unregister($io_ctx_thread & ctx); 135 138 void __ioctx_prepare_block($io_ctx_thread & ctx); 136 139 void __sqe_clean( volatile struct io_uring_sqe * sqe ); -
libcfa/src/concurrency/kernel.cfa
rb6a8b31 rd95969a 140 140 preemption_scope scope = { this }; 141 141 142 #if !defined(__CFA_NO_STATISTICS__) 143 unsigned long long last_tally = rdtscl(); 144 #endif 145 146 142 147 __cfadbg_print_safe(runtime_core, "Kernel : core %p started\n", this); 143 148 … … 206 211 // Are we done? 207 212 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 213 214 #if !defined(__CFA_NO_STATISTICS__) 215 unsigned long long curr = rdtscl(); 216 if(curr > (last_tally + 500000000)) { 217 __tally_stats(this->cltr->stats, __cfaabi_tls.this_stats); 218 last_tally = curr; 219 } 220 #endif 208 221 } 209 222 … … 211 224 } 212 225 213 V( this->terminated );226 post( this->terminated ); 214 227 215 228 if(this == mainProcessor) { … … 611 624 // Unexpected Terminating logic 612 625 //============================================================================================= 613 static __spinlock_t kernel_abort_lock; 614 static bool kernel_abort_called = false; 615 616 void * kernel_abort(void) __attribute__ ((__nothrow__)) { 617 // abort cannot be recursively entered by the same or different processors because all signal handlers return when 618 // the globalAbort flag is true. 619 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(); 623 624 // first task to abort ? 625 if ( kernel_abort_called ) { // not first task to abort ? 626 unlock( kernel_abort_lock ); 627 628 sigset_t mask; 629 sigemptyset( &mask ); 630 sigaddset( &mask, SIGALRM ); // block SIGALRM signals 631 sigaddset( &mask, SIGUSR1 ); // block SIGALRM signals 632 sigsuspend( &mask ); // block the processor to prevent further damage during abort 633 _exit( EXIT_FAILURE ); // if processor unblocks before it is killed, terminate it 634 } 635 else { 636 kernel_abort_called = true; 637 unlock( kernel_abort_lock ); 638 } 639 640 return __cfaabi_tls.this_thread; 641 } 642 643 void kernel_abort_msg( void * kernel_data, char * abort_text, int abort_text_size ) { 644 $thread * thrd = ( $thread * ) kernel_data; 626 void __kernel_abort_msg( char * abort_text, int abort_text_size ) { 627 $thread * thrd = __cfaabi_tls.this_thread; 645 628 646 629 if(thrd) { … … 662 645 } 663 646 664 int kernel_abort_lastframe( void ) __attribute__ ((__nothrow__)) {665 return get_coroutine( kernelTLS().this_thread) == get_coroutine(mainThread) ? 4 : 2;647 int __kernel_abort_lastframe( void ) __attribute__ ((__nothrow__)) { 648 return get_coroutine(__cfaabi_tls.this_thread) == get_coroutine(mainThread) ? 4 : 2; 666 649 } 667 650 … … 681 664 // Kernel Utilities 682 665 //============================================================================================= 683 //-----------------------------------------------------------------------------684 // Locks685 void ?{}( semaphore & this, int count = 1 ) {686 (this.lock){};687 this.count = count;688 (this.waiting){};689 }690 void ^?{}(semaphore & this) {}691 692 bool P(semaphore & this) with( this ){693 lock( lock __cfaabi_dbg_ctx2 );694 count -= 1;695 if ( count < 0 ) {696 // queue current task697 append( waiting, active_thread() );698 699 // atomically release spin lock and block700 unlock( lock );701 park();702 return true;703 }704 else {705 unlock( lock );706 return false;707 }708 }709 710 bool V(semaphore & this) with( this ) {711 $thread * thrd = 0p;712 lock( lock __cfaabi_dbg_ctx2 );713 count += 1;714 if ( count <= 0 ) {715 // remove task at head of waiting list716 thrd = pop_head( waiting );717 }718 719 unlock( lock );720 721 // make new owner722 unpark( thrd );723 724 return thrd != 0p;725 }726 727 bool 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;739 }740 741 666 //----------------------------------------------------------------------------- 742 667 // Debug -
libcfa/src/concurrency/kernel.hfa
rb6a8b31 rd95969a 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // kernel -- 7 // kernel -- Header containing the core of the kernel API 8 8 // 9 9 // Author : Thierry Delisle … … 24 24 extern "C" { 25 25 #include <bits/pthreadtypes.h> 26 #include <pthread.h> 26 27 #include <linux/types.h> 27 28 } 28 29 29 30 //----------------------------------------------------------------------------- 30 // Locks 31 struct semaphore { 32 __spinlock_t lock; 33 int count; 34 __queue_t($thread) waiting; 35 }; 36 37 void ?{}(semaphore & this, int count = 1); 38 void ^?{}(semaphore & this); 39 bool P (semaphore & this); 40 bool V (semaphore & this); 41 bool V (semaphore & this, unsigned count); 31 // Underlying Locks 32 #ifdef __CFA_WITH_VERIFY__ 33 extern bool __cfaabi_dbg_in_kernel(); 34 #endif 35 36 extern "C" { 37 char * strerror(int); 38 } 39 #define CHECKED(x) { int err = x; if( err != 0 ) abort("KERNEL ERROR: Operation \"" #x "\" return error %d - %s\n", err, strerror(err)); } 40 41 struct __bin_sem_t { 42 pthread_mutex_t lock; 43 pthread_cond_t cond; 44 int val; 45 }; 46 47 static inline void ?{}(__bin_sem_t & this) with( this ) { 48 // Create the mutex with error checking 49 pthread_mutexattr_t mattr; 50 pthread_mutexattr_init( &mattr ); 51 pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_ERRORCHECK_NP); 52 pthread_mutex_init(&lock, &mattr); 53 54 pthread_cond_init (&cond, (const pthread_condattr_t *)0p); // workaround trac#208: cast should not be required 55 val = 0; 56 } 57 58 static inline void ^?{}(__bin_sem_t & this) with( this ) { 59 CHECKED( pthread_mutex_destroy(&lock) ); 60 CHECKED( pthread_cond_destroy (&cond) ); 61 } 62 63 static inline void wait(__bin_sem_t & this) with( this ) { 64 verify(__cfaabi_dbg_in_kernel()); 65 CHECKED( pthread_mutex_lock(&lock) ); 66 while(val < 1) { 67 pthread_cond_wait(&cond, &lock); 68 } 69 val -= 1; 70 CHECKED( pthread_mutex_unlock(&lock) ); 71 } 72 73 static inline bool post(__bin_sem_t & this) with( this ) { 74 bool needs_signal = false; 75 76 CHECKED( pthread_mutex_lock(&lock) ); 77 if(val < 1) { 78 val += 1; 79 pthread_cond_signal(&cond); 80 needs_signal = true; 81 } 82 CHECKED( pthread_mutex_unlock(&lock) ); 83 84 return needs_signal; 85 } 86 87 #undef CHECKED 42 88 43 89 … … 91 137 92 138 // Termination synchronisation (user semaphore) 93 semaphoreterminated;139 oneshot terminated; 94 140 95 141 // pthread Stack -
libcfa/src/concurrency/kernel/fwd.hfa
rb6a8b31 rd95969a 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // kernel/fwd.hfa -- 7 // kernel/fwd.hfa -- PUBLIC 8 // Fundamental code needed to implement threading M.E.S. algorithms. 8 9 // 9 10 // Author : Thierry Delisle … … 134 135 extern uint64_t thread_rand(); 135 136 137 // Semaphore which only supports a single thread 138 struct single_sem { 139 struct $thread * volatile ptr; 140 }; 141 142 static inline { 143 void ?{}(single_sem & this) { 144 this.ptr = 0p; 145 } 146 147 void ^?{}(single_sem &) {} 148 149 bool wait(single_sem & this) { 150 for() { 151 struct $thread * expected = this.ptr; 152 if(expected == 1p) { 153 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 154 return false; 155 } 156 } 157 else { 158 /* paranoid */ verify( expected == 0p ); 159 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 160 park(); 161 return true; 162 } 163 } 164 165 } 166 } 167 168 bool post(single_sem & this) { 169 for() { 170 struct $thread * expected = this.ptr; 171 if(expected == 1p) return false; 172 if(expected == 0p) { 173 if(__atomic_compare_exchange_n(&this.ptr, &expected, 1p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 174 return false; 175 } 176 } 177 else { 178 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 179 unpark( expected ); 180 return true; 181 } 182 } 183 } 184 } 185 } 186 187 // Synchronozation primitive which only supports a single thread and one post 188 // Similar to a binary semaphore with a 'one shot' semantic 189 // is expected to be discarded after each party call their side 190 struct oneshot { 191 // Internal state : 192 // 0p : is initial state (wait will block) 193 // 1p : fulfilled (wait won't block) 194 // any thread : a thread is currently waiting 195 struct $thread * volatile ptr; 196 }; 197 198 static inline { 199 void ?{}(oneshot & this) { 200 this.ptr = 0p; 201 } 202 203 void ^?{}(oneshot &) {} 204 205 // Wait for the post, return immidiately if it already happened. 206 // return true if the thread was parked 207 bool wait(oneshot & this) { 208 for() { 209 struct $thread * expected = this.ptr; 210 if(expected == 1p) return false; 211 /* paranoid */ verify( expected == 0p ); 212 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 213 park(); 214 /* paranoid */ verify( this.ptr == 1p ); 215 return true; 216 } 217 } 218 } 219 220 // Mark as fulfilled, wake thread if needed 221 // return true if a thread was unparked 222 bool post(oneshot & this) { 223 struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 224 if( got == 0p ) return false; 225 unpark( got ); 226 return true; 227 } 228 } 229 230 // base types for future to build upon 231 // It is based on the 'oneshot' type to allow multiple futures 232 // to block on the same instance, permitting users to block a single 233 // thread on "any of" [a given set of] futures. 234 // does not support multiple threads waiting on the same future 235 struct future_t { 236 // Internal state : 237 // 0p : is initial state (wait will block) 238 // 1p : fulfilled (wait won't block) 239 // 2p : in progress () 240 // 3p : abandoned, server should delete 241 // any oneshot : a context has been setup to wait, a thread could wait on it 242 struct oneshot * volatile ptr; 243 }; 244 245 static inline { 246 void ?{}(future_t & this) { 247 this.ptr = 0p; 248 } 249 250 void ^?{}(future_t &) {} 251 252 void reset(future_t & this) { 253 // needs to be in 0p or 1p 254 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 255 } 256 257 // check if the future is available 258 bool available( future_t & this ) { 259 return this.ptr == 1p; 260 } 261 262 // Prepare the future to be waited on 263 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 264 bool setup( future_t & this, oneshot & wait_ctx ) { 265 /* paranoid */ verify( wait_ctx.ptr == 0p ); 266 // The future needs to set the wait context 267 for() { 268 struct oneshot * expected = this.ptr; 269 // Is the future already fulfilled? 270 if(expected == 1p) return false; // Yes, just return false (didn't block) 271 272 // The future is not fulfilled, try to setup the wait context 273 /* paranoid */ verify( expected == 0p ); 274 if(__atomic_compare_exchange_n(&this.ptr, &expected, &wait_ctx, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 275 return true; 276 } 277 } 278 } 279 280 // Stop waiting on a future 281 // When multiple futures are waited for together in "any of" pattern 282 // futures that weren't fulfilled before the thread woke up 283 // should retract the wait ctx 284 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 285 void retract( future_t & this, oneshot & wait_ctx ) { 286 // Remove the wait context 287 struct oneshot * got = __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 288 289 // got == 0p: future was never actually setup, just return 290 if( got == 0p ) return; 291 292 // got == wait_ctx: since fulfil does an atomic_swap, 293 // if we got back the original then no one else saw context 294 // It is safe to delete (which could happen after the return) 295 if( got == &wait_ctx ) return; 296 297 // got == 1p: the future is ready and the context was fully consumed 298 // the server won't use the pointer again 299 // It is safe to delete (which could happen after the return) 300 if( got == 1p ) return; 301 302 // got == 2p: the future is ready but the context hasn't fully been consumed 303 // spin until it is safe to move on 304 if( got == 2p ) { 305 while( this.ptr != 1p ) Pause(); 306 return; 307 } 308 309 // got == any thing else, something wen't wrong here, abort 310 abort("Future in unexpected state"); 311 } 312 313 // Mark the future as abandoned, meaning it will be deleted by the server 314 bool abandon( future_t & this ) { 315 /* paranoid */ verify( this.ptr != 3p ); 316 317 // Mark the future as abandonned 318 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST); 319 320 // If the future isn't already fulfilled, let the server delete it 321 if( got == 0p ) return false; 322 323 // got == 2p: the future is ready but the context hasn't fully been consumed 324 // spin until it is safe to move on 325 if( got == 2p ) { 326 while( this.ptr != 1p ) Pause(); 327 got = 1p; 328 } 329 330 // The future is completed delete it now 331 /* paranoid */ verify( this.ptr != 1p ); 332 free( &this ); 333 return true; 334 } 335 336 // from the server side, mark the future as fulfilled 337 // delete it if needed 338 bool fulfil( future_t & this ) { 339 for() { 340 struct oneshot * expected = this.ptr; 341 // was this abandoned? 342 #if defined(__GNUC__) && __GNUC__ >= 7 343 #pragma GCC diagnostic push 344 #pragma GCC diagnostic ignored "-Wfree-nonheap-object" 345 #endif 346 if( expected == 3p ) { free( &this ); return false; } 347 #if defined(__GNUC__) && __GNUC__ >= 7 348 #pragma GCC diagnostic pop 349 #endif 350 351 /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen 352 /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case. 353 354 // If there is a wait context, we need to consume it and mark it as consumed after 355 // If there is no context then we can skip the in progress phase 356 struct oneshot * want = expected == 0p ? 1p : 2p; 357 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 358 if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return false; } 359 bool ret = post( *expected ); 360 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 361 return ret; 362 } 363 } 364 365 } 366 367 // Wait for the future to be fulfilled 368 bool wait( future_t & this ) { 369 oneshot temp; 370 if( !setup(this, temp) ) return false; 371 372 // Wait context is setup, just wait on it 373 bool ret = wait( temp ); 374 375 // Wait for the future to tru 376 while( this.ptr == 2p ) Pause(); 377 // Make sure the state makes sense 378 // Should be fulfilled, could be in progress but it's out of date if so 379 // since if that is the case, the oneshot was fulfilled (unparking this thread) 380 // and the oneshot should not be needed any more 381 __attribute__((unused)) struct oneshot * was = this.ptr; 382 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was ); 383 384 // Mark the future as fulfilled, to be consistent 385 // with potential calls to avail 386 // this.ptr = 1p; 387 return ret; 388 } 389 } 390 136 391 //----------------------------------------------------------------------- 137 392 // Statics call at the end of each thread to register statistics -
libcfa/src/concurrency/kernel/startup.cfa
rb6a8b31 rd95969a 199 199 void ?{}(processor & this) with( this ) { 200 200 ( this.idle ){}; 201 ( this.terminated ){ 0};201 ( this.terminated ){}; 202 202 ( this.runner ){}; 203 203 init( this, "Main Processor", *mainCluster ); … … 528 528 void ?{}(processor & this, const char name[], cluster & _cltr) { 529 529 ( this.idle ){}; 530 ( this.terminated ){ 0};530 ( this.terminated ){}; 531 531 ( this.runner ){}; 532 532 … … 549 549 __wake_proc( &this ); 550 550 551 P( terminated );551 wait( terminated ); 552 552 /* paranoid */ verify( active_processor() != &this); 553 553 } -
libcfa/src/concurrency/locks.cfa
rb6a8b31 rd95969a 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // locks.hfa -- LIBCFATHREAD 8 // Runtime locks that used with the runtime thread system. 9 // 10 // Author : Colby Alexander Parsons 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 16 17 #define __cforall_thread__ 18 1 19 #include "locks.hfa" 2 20 #include "kernel_private.hfa" … … 7 25 //----------------------------------------------------------------------------- 8 26 // info_thread 9 forall( dtype L| is_blocking_lock(L)) {27 forall(L & | is_blocking_lock(L)) { 10 28 struct info_thread { 11 29 // used to put info_thread on a dl queue (aka sequence) … … 56 74 57 75 void ^?{}( blocking_lock & this ) {} 58 void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };} 59 void ^?{}( single_acquisition_lock & this ) {} 60 void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };} 61 void ^?{}( owner_lock & this ) {} 62 void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };} 63 void ^?{}( multiple_acquisition_lock & this ) {} 76 64 77 65 78 void lock( blocking_lock & this ) with( this ) { … … 170 183 171 184 //----------------------------------------------------------------------------- 172 // Overloaded routines for traits173 // These routines are temporary until an inheritance bug is fixed174 void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); }175 void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); }176 void on_wait ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); }177 void on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }178 void set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); }179 size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this ); }180 181 void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); }182 void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); }183 void on_wait ( owner_lock & this ) { on_wait( (blocking_lock &)this ); }184 void on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }185 void set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); }186 size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); }187 188 void lock ( multiple_acquisition_lock & this ) { lock ( (blocking_lock &)this ); }189 void unlock ( multiple_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); }190 void on_wait ( multiple_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); }191 void on_notify( multiple_acquisition_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); }192 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }193 size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }194 195 //-----------------------------------------------------------------------------196 185 // alarm node wrapper 197 forall( dtype L| is_blocking_lock(L)) {186 forall(L & | is_blocking_lock(L)) { 198 187 struct alarm_node_wrap { 199 188 alarm_node_t alarm_node; … … 239 228 //----------------------------------------------------------------------------- 240 229 // condition variable 241 forall( dtype L| is_blocking_lock(L)) {230 forall(L & | is_blocking_lock(L)) { 242 231 243 232 void ?{}( condition_variable(L) & this ){ … … 356 345 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) { WAIT_TIME( info, &l , time ) } 357 346 } 347 348 //----------------------------------------------------------------------------- 349 // Semaphore 350 void ?{}( semaphore & this, int count = 1 ) { 351 (this.lock){}; 352 this.count = count; 353 (this.waiting){}; 354 } 355 void ^?{}(semaphore & this) {} 356 357 bool P(semaphore & this) with( this ){ 358 lock( lock __cfaabi_dbg_ctx2 ); 359 count -= 1; 360 if ( count < 0 ) { 361 // queue current task 362 append( waiting, active_thread() ); 363 364 // atomically release spin lock and block 365 unlock( lock ); 366 park(); 367 return true; 368 } 369 else { 370 unlock( lock ); 371 return false; 372 } 373 } 374 375 bool V(semaphore & this) with( this ) { 376 $thread * thrd = 0p; 377 lock( lock __cfaabi_dbg_ctx2 ); 378 count += 1; 379 if ( count <= 0 ) { 380 // remove task at head of waiting list 381 thrd = pop_head( waiting ); 382 } 383 384 unlock( lock ); 385 386 // make new owner 387 unpark( thrd ); 388 389 return thrd != 0p; 390 } 391 392 bool V(semaphore & this, unsigned diff) with( this ) { 393 $thread * thrd = 0p; 394 lock( lock __cfaabi_dbg_ctx2 ); 395 int release = max(-count, (int)diff); 396 count += diff; 397 for(release) { 398 unpark( pop_head( waiting ) ); 399 } 400 401 unlock( lock ); 402 403 return thrd != 0p; 404 } -
libcfa/src/concurrency/locks.hfa
rb6a8b31 rd95969a 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // locks.hfa -- PUBLIC 8 // Runtime locks that used with the runtime thread system. 9 // 10 // Author : Colby Alexander Parsons 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 16 1 17 #pragma once 2 18 3 19 #include <stdbool.h> 4 20 5 #include "bits/locks.hfa" 6 #include "bits/sequence.hfa" 7 8 #include "invoke.h" 21 #include "bits/weakso_locks.hfa" 9 22 10 23 #include "time_t.hfa" 11 24 #include "time.hfa" 12 25 26 //---------- 27 struct single_acquisition_lock { 28 inline blocking_lock; 29 }; 30 31 static inline void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };} 32 static inline void ^?{}( single_acquisition_lock & this ) {} 33 static inline void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 34 static inline void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 35 static inline void on_wait ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); } 36 static inline void on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 37 static inline void set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 38 static inline size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 39 40 //---------- 41 struct owner_lock { 42 inline blocking_lock; 43 }; 44 45 static inline void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };} 46 static inline void ^?{}( owner_lock & this ) {} 47 static inline void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); } 48 static inline void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); } 49 static inline void on_wait ( owner_lock & this ) { on_wait( (blocking_lock &)this ); } 50 static inline void on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 51 static inline void set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 52 static inline size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 53 13 54 //----------------------------------------------------------------------------- 14 55 // is_blocking_lock 15 trait is_blocking_lock( dtype L| sized(L)) {56 trait is_blocking_lock(L & | sized(L)) { 16 57 // For synchronization locks to use when acquiring 17 58 void on_notify( L &, struct $thread * ); … … 31 72 // the info thread is a wrapper around a thread used 32 73 // to store extra data for use in the condition variable 33 forall( dtype L| is_blocking_lock(L)) {74 forall(L & | is_blocking_lock(L)) { 34 75 struct info_thread; 35 76 … … 40 81 41 82 //----------------------------------------------------------------------------- 42 // Blocking Locks43 struct blocking_lock {44 // Spin lock used for mutual exclusion45 __spinlock_t lock;46 47 // List of blocked threads48 Sequence( $thread ) blocked_threads;49 50 // Count of current blocked threads51 size_t wait_count;52 53 // Flag if the lock allows multiple acquisition54 bool multi_acquisition;55 56 // Flag if lock can be released by non owner57 bool strict_owner;58 59 // Current thread owning the lock60 struct $thread * owner;61 62 // Number of recursion level63 size_t recursion_count;64 };65 66 struct single_acquisition_lock {67 inline blocking_lock;68 };69 70 struct owner_lock {71 inline blocking_lock;72 };73 74 struct multiple_acquisition_lock {75 inline blocking_lock;76 };77 78 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner );79 void ^?{}( blocking_lock & this );80 81 void ?{}( single_acquisition_lock & this );82 void ^?{}( single_acquisition_lock & this );83 84 void ?{}( owner_lock & this );85 void ^?{}( owner_lock & this );86 87 void ?{}( multiple_acquisition_lock & this );88 void ^?{}( multiple_acquisition_lock & this );89 90 void lock( blocking_lock & this );91 bool try_lock( blocking_lock & this );92 void unlock( blocking_lock & this );93 void on_notify( blocking_lock & this, struct $thread * t );94 void on_wait( blocking_lock & this );95 size_t wait_count( blocking_lock & this );96 void set_recursion_count( blocking_lock & this, size_t recursion );97 size_t get_recursion_count( blocking_lock & this );98 99 void lock( single_acquisition_lock & this );100 void unlock( single_acquisition_lock & this );101 void on_notify( single_acquisition_lock & this, struct $thread * t );102 void on_wait( single_acquisition_lock & this );103 void set_recursion_count( single_acquisition_lock & this, size_t recursion );104 size_t get_recursion_count( single_acquisition_lock & this );105 106 void lock( owner_lock & this );107 void unlock( owner_lock & this );108 void on_notify( owner_lock & this, struct $thread * t );109 void on_wait( owner_lock & this );110 void set_recursion_count( owner_lock & this, size_t recursion );111 size_t get_recursion_count( owner_lock & this );112 113 void lock( multiple_acquisition_lock & this );114 void unlock( multiple_acquisition_lock & this );115 void on_notify( multiple_acquisition_lock & this, struct $thread * t );116 void on_wait( multiple_acquisition_lock & this );117 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion );118 size_t get_recursion_count( multiple_acquisition_lock & this );119 120 //-----------------------------------------------------------------------------121 83 // Synchronization Locks 122 forall( dtype L| is_blocking_lock(L)) {84 forall(L & | is_blocking_lock(L)) { 123 85 struct condition_variable { 124 86 // Spin lock used for mutual exclusion … … 157 119 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ); 158 120 } 121 122 //----------------------------------------------------------------------------- 123 // Semaphore 124 struct semaphore { 125 __spinlock_t lock; 126 int count; 127 __queue_t($thread) waiting; 128 }; 129 130 void ?{}(semaphore & this, int count = 1); 131 void ^?{}(semaphore & this); 132 bool P (semaphore & this); 133 bool V (semaphore & this); 134 bool V (semaphore & this, unsigned count); -
libcfa/src/concurrency/monitor.cfa
rb6a8b31 rd95969a 50 50 static inline [$thread *, int] search_entry_queue( const __waitfor_mask_t &, $monitor * monitors [], __lock_size_t count ); 51 51 52 forall( dtype T| sized( T ))52 forall(T & | sized( T )) 53 53 static inline __lock_size_t insert_unique( T * array [], __lock_size_t & size, T * val ); 54 54 static inline __lock_size_t count_max ( const __waitfor_mask_t & mask ); … … 949 949 } 950 950 951 forall( dtype T| sized( T ))951 forall(T & | sized( T )) 952 952 static inline __lock_size_t insert_unique( T * array [], __lock_size_t & size, T * val ) { 953 953 if( !val ) return size; -
libcfa/src/concurrency/monitor.hfa
rb6a8b31 rd95969a 22 22 #include "stdlib.hfa" 23 23 24 trait is_monitor( dtype T) {24 trait is_monitor(T &) { 25 25 $monitor * get_monitor( T & ); 26 26 void ^?{}( T & mutex ); … … 59 59 void ^?{}( monitor_dtor_guard_t & this ); 60 60 61 static inline forall( dtype T| sized(T) | { void ^?{}( T & mutex ); } )61 static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } ) 62 62 void delete( T * th ) { 63 63 ^(*th){}; -
libcfa/src/concurrency/mutex.cfa
rb6a8b31 rd95969a 164 164 } 165 165 166 forall( dtype L| is_lock(L))166 forall(L & | is_lock(L)) 167 167 void wait(condition_variable & this, L & l) { 168 168 lock( this.lock __cfaabi_dbg_ctx2 ); … … 176 176 //----------------------------------------------------------------------------- 177 177 // Scopes 178 forall( dtype L| is_lock(L))178 forall(L & | is_lock(L)) 179 179 void lock_all ( L * locks[], size_t count) { 180 180 // Sort locks based on addresses … … 188 188 } 189 189 190 forall( dtype L| is_lock(L))190 forall(L & | is_lock(L)) 191 191 void unlock_all( L * locks[], size_t count) { 192 192 // Lock all -
libcfa/src/concurrency/mutex.hfa
rb6a8b31 rd95969a 42 42 }; 43 43 44 void ?{}(mutex_lock & this) ;45 void ^?{}(mutex_lock & this) ;46 void lock(mutex_lock & this) ;47 bool try_lock(mutex_lock & this) ;48 void unlock(mutex_lock & this) ;44 void ?{}(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 45 void ^?{}(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 46 void lock(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 47 bool try_lock(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 48 void unlock(mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 49 49 50 50 // Exclusive lock - recursive … … 64 64 }; 65 65 66 void ?{}(recursive_mutex_lock & this) ;67 void ^?{}(recursive_mutex_lock & this) ;68 void lock(recursive_mutex_lock & this) ;69 bool try_lock(recursive_mutex_lock & this) ;70 void unlock(recursive_mutex_lock & this) ;66 void ?{}(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 67 void ^?{}(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 68 void lock(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 69 bool try_lock(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 70 void unlock(recursive_mutex_lock & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 71 71 72 trait is_lock( dtype L| sized(L)) {72 trait is_lock(L & | sized(L)) { 73 73 void lock (L &); 74 74 void unlock(L &); … … 86 86 }; 87 87 88 void ?{}(condition_variable & this) ;89 void ^?{}(condition_variable & this) ;88 void ?{}(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 89 void ^?{}(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 90 90 91 void notify_one(condition_variable & this) ;92 void notify_all(condition_variable & this) ;91 void notify_one(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 92 void notify_all(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 93 93 94 void wait(condition_variable & this) ;94 void wait(condition_variable & this) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 95 95 96 forall( dtype L| is_lock(L))97 void wait(condition_variable & this, L & l) ;96 forall(L & | is_lock(L)) 97 void wait(condition_variable & this, L & l) __attribute__((deprecated("use concurrency/locks.hfa instead"))); 98 98 99 99 //----------------------------------------------------------------------------- 100 100 // Scopes 101 forall( dtype L| is_lock(L)) {101 forall(L & | is_lock(L)) { 102 102 #if !defined( __TUPLE_ARRAYS_EXIST__ ) 103 103 void lock ( L * locks [], size_t count); -
libcfa/src/concurrency/preemption.cfa
rb6a8b31 rd95969a 616 616 } 617 617 618 // Prevent preemption since we are about to start terminating things 619 void __kernel_abort_lock(void) { 620 signal_block( SIGUSR1 ); 621 } 622 618 623 // Raii ctor/dtor for the preemption_scope 619 624 // Used by thread to control when they want to receive preemption signals -
libcfa/src/concurrency/stats.hfa
rb6a8b31 rd95969a 2 2 3 3 #include <stdint.h> 4 5 enum { 6 CFA_STATS_READY_Q = 0x01, 7 CFA_STATS_IO = 0x02, 8 }; 4 9 5 10 #if defined(__CFA_NO_STATISTICS__) … … 9 14 static inline void __print_stats( struct __stats_t *, int, const char *, const char *, void * ) {} 10 15 #else 11 enum {12 CFA_STATS_READY_Q = 0x01,13 #if defined(CFA_HAVE_LINUX_IO_URING_H)14 CFA_STATS_IO = 0x02,15 #endif16 };17 16 18 17 struct __attribute__((aligned(64))) __stats_readQ_t { -
libcfa/src/concurrency/thread.cfa
rb6a8b31 rd95969a 62 62 } 63 63 64 FORALL_DATA_INSTANCE(ThreadCancelled, ( dtype thread_t), (thread_t))64 FORALL_DATA_INSTANCE(ThreadCancelled, (thread_t &), (thread_t)) 65 65 66 forall( dtype T)66 forall(T &) 67 67 void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src) { 68 68 dst->virtual_table = src->virtual_table; … … 71 71 } 72 72 73 forall( dtype T)73 forall(T &) 74 74 const char * msg(ThreadCancelled(T) *) { 75 75 return "ThreadCancelled"; 76 76 } 77 77 78 forall( dtype T)78 forall(T &) 79 79 static void default_thread_cancel_handler(ThreadCancelled(T) & ) { 80 80 abort( "Unhandled thread cancellation.\n" ); 81 81 } 82 82 83 forall( dtype T| is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)))83 forall(T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T))) 84 84 void ?{}( thread_dtor_guard_t & this, 85 T & thrd, void(* defaultResumptionHandler)(ThreadCancelled(T) &)) {86 $monitor * m = get_monitor(thrd);85 T & thrd, void(*cancelHandler)(ThreadCancelled(T) &)) { 86 $monitor * m = get_monitor(thrd); 87 87 $thread * desc = get_thread(thrd); 88 88 89 89 // Setup the monitor guard 90 90 void (*dtor)(T& mutex this) = ^?{}; 91 bool join = defaultResumptionHandler != (void(*)(ThreadCancelled(T)&))0;91 bool join = cancelHandler != (void(*)(ThreadCancelled(T)&))0; 92 92 (this.mg){&m, (void(*)())dtor, join}; 93 93 … … 103 103 } 104 104 desc->state = Cancelled; 105 if (!join) { 106 defaultResumptionHandler = default_thread_cancel_handler; 107 } 105 void(*defaultResumptionHandler)(ThreadCancelled(T) &) = 106 join ? cancelHandler : default_thread_cancel_handler; 108 107 109 108 ThreadCancelled(T) except; … … 125 124 //----------------------------------------------------------------------------- 126 125 // Starting and stopping threads 127 forall( dtype T| is_thread(T) )126 forall( T & | is_thread(T) ) 128 127 void __thrd_start( T & this, void (*main_p)(T &) ) { 129 128 $thread * this_thrd = get_thread(this); … … 141 140 //----------------------------------------------------------------------------- 142 141 // Support for threads that don't ues the thread keyword 143 forall( dtype T| sized(T) | is_thread(T) | { void ?{}(T&); } )142 forall( T & | sized(T) | is_thread(T) | { void ?{}(T&); } ) 144 143 void ?{}( scoped(T)& this ) with( this ) { 145 144 handle{}; … … 147 146 } 148 147 149 forall( dtype T, ttype P| sized(T) | is_thread(T) | { void ?{}(T&, P); } )148 forall( T &, P... | sized(T) | is_thread(T) | { void ?{}(T&, P); } ) 150 149 void ?{}( scoped(T)& this, P params ) with( this ) { 151 150 handle{ params }; … … 153 152 } 154 153 155 forall( dtype T| sized(T) | is_thread(T) )154 forall( T & | sized(T) | is_thread(T) ) 156 155 void ^?{}( scoped(T)& this ) with( this ) { 157 156 ^handle{}; … … 159 158 160 159 //----------------------------------------------------------------------------- 161 forall( dtype T| is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)))160 forall(T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T))) 162 161 T & join( T & this ) { 163 162 thread_dtor_guard_t guard = { this, defaultResumptionHandler }; -
libcfa/src/concurrency/thread.hfa
rb6a8b31 rd95969a 26 26 //----------------------------------------------------------------------------- 27 27 // thread trait 28 trait is_thread( dtype T) {28 trait is_thread(T &) { 29 29 void ^?{}(T& mutex this); 30 30 void main(T& this); … … 32 32 }; 33 33 34 FORALL_DATA_EXCEPTION(ThreadCancelled, ( dtype thread_t), (thread_t)) (34 FORALL_DATA_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) ( 35 35 thread_t * the_thread; 36 36 exception_t * the_exception; 37 37 ); 38 38 39 forall( dtype T)39 forall(T &) 40 40 void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src); 41 41 42 forall( dtype T)42 forall(T &) 43 43 const char * msg(ThreadCancelled(T) *); 44 44 … … 47 47 48 48 // Inline getters for threads/coroutines/monitors 49 forall( dtype T| is_thread(T) )49 forall( T & | is_thread(T) ) 50 50 static inline $coroutine* get_coroutine(T & this) __attribute__((const)) { return &get_thread(this)->self_cor; } 51 51 52 forall( dtype T| is_thread(T) )52 forall( T & | is_thread(T) ) 53 53 static inline $monitor * get_monitor (T & this) __attribute__((const)) { return &get_thread(this)->self_mon; } 54 54 … … 60 60 extern struct cluster * mainCluster; 61 61 62 forall( dtype T| is_thread(T) )62 forall( T & | is_thread(T) ) 63 63 void __thrd_start( T & this, void (*)(T &) ); 64 64 … … 82 82 }; 83 83 84 forall( dtype T| is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) )84 forall( T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) ) 85 85 void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(ThreadCancelled(T) &) ); 86 86 void ^?{}( thread_dtor_guard_t & this ); … … 89 89 // thread runner 90 90 // Structure that actually start and stop threads 91 forall( dtype T| sized(T) | is_thread(T) )91 forall( T & | sized(T) | is_thread(T) ) 92 92 struct scoped { 93 93 T handle; 94 94 }; 95 95 96 forall( dtype T| sized(T) | is_thread(T) | { void ?{}(T&); } )96 forall( T & | sized(T) | is_thread(T) | { void ?{}(T&); } ) 97 97 void ?{}( scoped(T)& this ); 98 98 99 forall( dtype T, ttype P| sized(T) | is_thread(T) | { void ?{}(T&, P); } )99 forall( T &, P... | sized(T) | is_thread(T) | { void ?{}(T&, P); } ) 100 100 void ?{}( scoped(T)& this, P params ); 101 101 102 forall( dtype T| sized(T) | is_thread(T) )102 forall( T & | sized(T) | is_thread(T) ) 103 103 void ^?{}( scoped(T)& this ); 104 104 … … 115 115 void unpark( $thread * this ); 116 116 117 forall( dtype T| is_thread(T) )117 forall( T & | is_thread(T) ) 118 118 static inline void unpark( T & this ) { if(!&this) return; unpark( get_thread( this ) );} 119 119 … … 128 128 //---------- 129 129 // join 130 forall( dtype T| is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) )130 forall( T & | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) ) 131 131 T & join( T & this ); 132 132 -
libcfa/src/containers/list.hfa
rb6a8b31 rd95969a 66 66 #define __DLISTED_MGD_JUSTIMPL(STRUCT) 67 67 68 forall( dtype tE) {68 forall( tE & ) { 69 69 struct $mgd_link { 70 70 tE *elem; … … 83 83 (this.is_terminator){ 1 }; 84 84 } 85 forall ( otypetInit | { void ?{}( $mgd_link(tE) &, tInit); } )85 forall ( tInit | { void ?{}( $mgd_link(tE) &, tInit); } ) 86 86 static inline void ?=?( $mgd_link(tE) &this, tInit i ) { 87 87 ^?{}( this ); … … 115 115 __DLISTED_MGD_COMMON(STRUCT, STRUCT, $links) 116 116 117 trait $dlistable( dtype Tnode, dtype Telem) {117 trait $dlistable(Tnode &, Telem &) { 118 118 $mgd_link(Telem) & $prev_link(Tnode &); 119 119 $mgd_link(Telem) & $next_link(Tnode &); … … 125 125 }; 126 126 127 forall ( dtype Tnode, dtype Telem| $dlistable(Tnode, Telem)) {127 forall (Tnode &, Telem & | $dlistable(Tnode, Telem)) { 128 128 129 129 // implemented as a sentinel item in an underlying cicrular list -
libcfa/src/containers/maybe.cfa
rb6a8b31 rd95969a 18 18 19 19 20 forall( otypeT)20 forall(T) 21 21 void ?{}(maybe(T) & this) { 22 22 this.has_value = false; 23 23 } 24 24 25 forall( otypeT)25 forall(T) 26 26 void ?{}(maybe(T) & this, T value) { 27 27 this.has_value = true; … … 29 29 } 30 30 31 forall( otypeT)31 forall(T) 32 32 void ?{}(maybe(T) & this, maybe(T) other) { 33 33 this.has_value = other.has_value; … … 37 37 } 38 38 39 forall( otypeT)39 forall(T) 40 40 maybe(T) ?=?(maybe(T) & this, maybe(T) that) { 41 41 if (this.has_value && that.has_value) { … … 51 51 } 52 52 53 forall( otypeT)53 forall(T) 54 54 void ^?{}(maybe(T) & this) { 55 55 if (this.has_value) { … … 58 58 } 59 59 60 forall( otypeT)60 forall(T) 61 61 bool ?!=?(maybe(T) this, zero_t) { 62 62 return this.has_value; 63 63 } 64 64 65 forall( otypeT)65 forall(T) 66 66 maybe(T) maybe_value(T value) { 67 67 return (maybe(T)){value}; 68 68 } 69 69 70 forall( otypeT)70 forall(T) 71 71 maybe(T) maybe_none() { 72 72 return (maybe(T)){}; 73 73 } 74 74 75 forall( otypeT)75 forall(T) 76 76 bool has_value(maybe(T) * this) { 77 77 return this->has_value; 78 78 } 79 79 80 forall( otypeT)80 forall(T) 81 81 T get(maybe(T) * this) { 82 82 assertf(this->has_value, "attempt to get from maybe without value"); … … 84 84 } 85 85 86 forall( otypeT)86 forall(T) 87 87 void set(maybe(T) * this, T value) { 88 88 if (this->has_value) { … … 94 94 } 95 95 96 forall( otypeT)96 forall(T) 97 97 void set_none(maybe(T) * this) { 98 98 if (this->has_value) { -
libcfa/src/containers/maybe.hfa
rb6a8b31 rd95969a 19 19 20 20 // DO NOT USE DIRECTLY! 21 forall( otypeT)21 forall(T) 22 22 struct maybe { 23 23 bool has_value; … … 26 26 27 27 28 forall( otypeT)28 forall(T) 29 29 void ?{}(maybe(T) & this); 30 30 31 forall( otypeT)31 forall(T) 32 32 void ?{}(maybe(T) & this, T value); 33 33 34 forall( otypeT)34 forall(T) 35 35 void ?{}(maybe(T) & this, maybe(T) other); 36 36 37 forall( otypeT)37 forall(T) 38 38 void ^?{}(maybe(T) & this); 39 39 40 forall( otypeT)40 forall(T) 41 41 maybe(T) ?=?(maybe(T) & this, maybe(T) other); 42 42 43 forall( otypeT)43 forall(T) 44 44 bool ?!=?(maybe(T) this, zero_t); 45 45 46 46 /* Waiting for bug#11 to be fixed. 47 forall( otypeT)47 forall(T) 48 48 maybe(T) maybe_value(T value); 49 49 50 forall( otypeT)50 forall(T) 51 51 maybe(T) maybe_none(); 52 52 */ 53 53 54 forall( otypeT)54 forall(T) 55 55 bool has_value(maybe(T) * this); 56 56 57 forall( otypeT)57 forall(T) 58 58 T get(maybe(T) * this); 59 59 60 forall( otypeT)60 forall(T) 61 61 void set(maybe(T) * this, T value); 62 62 63 forall( otypeT)63 forall(T) 64 64 void set_none(maybe(T) * this); 65 65 -
libcfa/src/containers/pair.cfa
rb6a8b31 rd95969a 13 13 #include <containers/pair.hfa> 14 14 15 forall( otype R, otypeS15 forall(R, S 16 16 | { int ?==?(R, R); int ?<?(R, R); int ?<?(S, S); }) 17 17 int ?<?(pair(R, S) p, pair(R, S) q) { … … 19 19 } 20 20 21 forall( otype R, otypeS21 forall(R, S 22 22 | { int ?==?(R, R); int ?<?(R, R); int ?<=?(S, S); }) 23 23 int ?<=?(pair(R, S) p, pair(R, S) q) { … … 25 25 } 26 26 27 forall( otype R, otypeS | { int ?==?(R, R); int ?==?(S, S); })27 forall(R, S | { int ?==?(R, R); int ?==?(S, S); }) 28 28 int ?==?(pair(R, S) p, pair(R, S) q) { 29 29 return p.first == q.first && p.second == q.second; 30 30 } 31 31 32 forall( otype R, otypeS | { int ?!=?(R, R); int ?!=?(S, S); })32 forall(R, S | { int ?!=?(R, R); int ?!=?(S, S); }) 33 33 int ?!=?(pair(R, S) p, pair(R, S) q) { 34 34 return p.first != q.first || p.second != q.second; 35 35 } 36 36 37 forall( otype R, otypeS37 forall(R, S 38 38 | { int ?==?(R, R); int ?>?(R, R); int ?>?(S, S); }) 39 39 int ?>?(pair(R, S) p, pair(R, S) q) { … … 41 41 } 42 42 43 forall( otype R, otypeS43 forall(R, S 44 44 | { int ?==?(R, R); int ?>?(R, R); int ?>=?(S, S); }) 45 45 int ?>=?(pair(R, S) p, pair(R, S) q) { -
libcfa/src/containers/pair.hfa
rb6a8b31 rd95969a 16 16 #pragma once 17 17 18 forall( otype R, otypeS) struct pair {18 forall(R, S) struct pair { 19 19 R first; 20 20 S second; 21 21 }; 22 22 23 forall( otype R, otypeS23 forall(R, S 24 24 | { int ?==?(R, R); int ?<?(R, R); int ?<?(S, S); }) 25 25 int ?<?(pair(R, S) p, pair(R, S) q); 26 26 27 forall( otype R, otypeS27 forall(R, S 28 28 | { int ?==?(R, R); int ?<?(R, R); int ?<=?(S, S); }) 29 29 int ?<=?(pair(R, S) p, pair(R, S) q); 30 30 31 forall( otype R, otypeS | { int ?==?(R, R); int ?==?(S, S); })31 forall(R, S | { int ?==?(R, R); int ?==?(S, S); }) 32 32 int ?==?(pair(R, S) p, pair(R, S) q); 33 33 34 forall( otype R, otypeS | { int ?!=?(R, R); int ?!=?(S, S); })34 forall(R, S | { int ?!=?(R, R); int ?!=?(S, S); }) 35 35 int ?!=?(pair(R, S) p, pair(R, S) q); 36 36 37 forall( otype R, otypeS37 forall(R, S 38 38 | { int ?==?(R, R); int ?>?(R, R); int ?>?(S, S); }) 39 39 int ?>?(pair(R, S) p, pair(R, S) q); 40 40 41 forall( otype R, otypeS41 forall(R, S 42 42 | { int ?==?(R, R); int ?>?(R, R); int ?>=?(S, S); }) 43 43 int ?>=?(pair(R, S) p, pair(R, S) q); -
libcfa/src/containers/result.cfa
rb6a8b31 rd95969a 18 18 19 19 20 forall( otype T, otypeE)20 forall(T, E) 21 21 void ?{}(result(T, E) & this) { 22 22 this.has_value = false; … … 24 24 } 25 25 26 forall( otype T, otypeE)26 forall(T, E) 27 27 void ?{}(result(T, E) & this, one_t, T value) { 28 28 this.has_value = true; … … 30 30 } 31 31 32 forall( otype T, otypeE)32 forall(T, E) 33 33 void ?{}(result(T, E) & this, zero_t, E error) { 34 34 this.has_value = false; … … 36 36 } 37 37 38 forall( otype T, otypeE)38 forall(T, E) 39 39 void ?{}(result(T, E) & this, result(T, E) other) { 40 40 this.has_value = other.has_value; … … 46 46 } 47 47 48 forall( otype T, otypeE)48 forall(T, E) 49 49 result(T, E) ?=?(result(T, E) & this, result(T, E) that) { 50 50 if (this.has_value && that.has_value) { … … 63 63 } 64 64 65 forall( otype T, otypeE)65 forall(T, E) 66 66 void ^?{}(result(T, E) & this) { 67 67 if (this.has_value) { … … 72 72 } 73 73 74 forall( otype T, otypeE)74 forall(T, E) 75 75 bool ?!=?(result(T, E) this, zero_t) { 76 76 return this.has_value; 77 77 } 78 78 79 forall( otype T, otypeE)79 forall(T, E) 80 80 result(T, E) result_value(T value) { 81 81 return (result(T, E)){1, value}; 82 82 } 83 83 84 forall( otype T, otypeE)84 forall(T, E) 85 85 result(T, E) result_error(E error) { 86 86 return (result(T, E)){0, error}; 87 87 } 88 88 89 forall( otype T, otypeE)89 forall(T, E) 90 90 bool has_value(result(T, E) * this) { 91 91 return this->has_value; 92 92 } 93 93 94 forall( otype T, otypeE)94 forall(T, E) 95 95 T get(result(T, E) * this) { 96 96 assertf(this->has_value, "attempt to get from result without value"); … … 98 98 } 99 99 100 forall( otype T, otypeE)100 forall(T, E) 101 101 E get_error(result(T, E) * this) { 102 102 assertf(!this->has_value, "attempt to get from result without error"); … … 104 104 } 105 105 106 forall( otype T, otypeE)106 forall(T, E) 107 107 void set(result(T, E) * this, T value) { 108 108 if (this->has_value) { … … 115 115 } 116 116 117 forall( otype T, otypeE)117 forall(T, E) 118 118 void set_error(result(T, E) * this, E error) { 119 119 if (this->has_value) { -
libcfa/src/containers/result.hfa
rb6a8b31 rd95969a 19 19 20 20 // DO NOT USE DIRECTLY! 21 forall( otype T, otypeE)21 forall(T, E) 22 22 union inner_result{ 23 23 T value; … … 25 25 }; 26 26 27 forall( otype T, otypeE)27 forall(T, E) 28 28 struct result { 29 29 bool has_value; … … 32 32 33 33 34 forall( otype T, otypeE)34 forall(T, E) 35 35 void ?{}(result(T, E) & this); 36 36 37 forall( otype T, otypeE)37 forall(T, E) 38 38 void ?{}(result(T, E) & this, one_t, T value); 39 39 40 forall( otype T, otypeE)40 forall(T, E) 41 41 void ?{}(result(T, E) & this, zero_t, E error); 42 42 43 forall( otype T, otypeE)43 forall(T, E) 44 44 void ?{}(result(T, E) & this, result(T, E) other); 45 45 46 forall( otype T, otypeE)46 forall(T, E) 47 47 void ^?{}(result(T, E) & this); 48 48 49 forall( otype T, otypeE)49 forall(T, E) 50 50 result(T, E) ?=?(result(T, E) & this, result(T, E) other); 51 51 52 forall( otype T, otypeE)52 forall(T, E) 53 53 bool ?!=?(result(T, E) this, zero_t); 54 54 55 55 /* Wating for bug#11 to be fixed. 56 forall( otype T, otypeE)56 forall(T, E) 57 57 result(T, E) result_value(T value); 58 58 59 forall( otype T, otypeE)59 forall(T, E) 60 60 result(T, E) result_error(E error); 61 61 */ 62 62 63 forall( otype T, otypeE)63 forall(T, E) 64 64 bool has_value(result(T, E) * this); 65 65 66 forall( otype T, otypeE)66 forall(T, E) 67 67 T get(result(T, E) * this); 68 68 69 forall( otype T, otypeE)69 forall(T, E) 70 70 E get_error(result(T, E) * this); 71 71 72 forall( otype T, otypeE)72 forall(T, E) 73 73 void set(result(T, E) * this, T value); 74 74 75 forall( otype T, otypeE)75 forall(T, E) 76 76 void set_error(result(T, E) * this, E error); 77 77 -
libcfa/src/containers/stackLockFree.hfa
rb6a8b31 rd95969a 9 9 // Created On : Wed May 13 20:58:58 2020 10 10 // Last Modified By : Peter A. Buhr 11 // Last Modified On : Sun Jun 14 13:25:09 202012 // Update Count : 6 411 // Last Modified On : Wed Jan 20 20:40:03 2021 12 // Update Count : 67 13 13 // 14 14 … … 17 17 #include <stdint.h> 18 18 19 forall( dtype T)19 forall( T & ) 20 20 union Link { 21 21 struct { // 32/64-bit x 2 … … 31 31 }; // Link 32 32 33 forall( otypeT | sized(T) | { Link(T) * ?`next( T * ); } ) {33 forall( T | sized(T) | { Link(T) * ?`next( T * ); } ) { 34 34 struct StackLF { 35 35 Link(T) stack; … … 42 42 43 43 void push( StackLF(T) & this, T & n ) with(this) { 44 *( &n )`next = stack; // atomic assignment unnecessary, or use CAA44 *( &n )`next = stack; // atomic assignment unnecessary, or use CAA 45 45 for () { // busy wait 46 46 if ( __atomic_compare_exchange_n( &stack.atom, &( &n )`next->atom, (Link(T))@{ {&n, ( &n )`next->count + 1} }.atom, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) break; // attempt to update top node … … 65 65 } 66 66 if( next == 0p ) return false; 67 link = ( next)`next;67 link = ( next )`next; 68 68 } 69 69 } -
libcfa/src/containers/vector.cfa
rb6a8b31 rd95969a 18 18 #include <stdlib.hfa> 19 19 20 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))20 forall(T, allocator_t | allocator_c(T, allocator_t)) 21 21 void copy_internal(vector(T, allocator_t)* this, vector(T, allocator_t)* other); 22 22 23 23 //------------------------------------------------------------------------------ 24 24 //Initialization 25 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))25 forall(T, allocator_t | allocator_c(T, allocator_t)) 26 26 void ?{}(vector(T, allocator_t)& this) 27 27 { … … 30 30 } 31 31 32 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))32 forall(T, allocator_t | allocator_c(T, allocator_t)) 33 33 void ?{}(vector(T, allocator_t)& this, vector(T, allocator_t) rhs) 34 34 { … … 37 37 } 38 38 39 // forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))39 // forall(T, allocator_t | allocator_c(T, allocator_t)) 40 40 // vector(T, allocator_t) ?=?(vector(T, allocator_t)* this, vector(T, allocator_t) rhs) 41 41 // { … … 45 45 // } 46 46 47 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))47 forall(T, allocator_t | allocator_c(T, allocator_t)) 48 48 void ^?{}(vector(T, allocator_t)& this) 49 49 { … … 54 54 //------------------------------------------------------------------------------ 55 55 //Modifiers 56 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))56 forall(T, allocator_t | allocator_c(T, allocator_t)) 57 57 void push_back(vector(T, allocator_t)* this, T value) 58 58 { … … 62 62 } 63 63 64 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))64 forall(T, allocator_t | allocator_c(T, allocator_t)) 65 65 void pop_back(vector(T, allocator_t)* this) 66 66 { … … 69 69 } 70 70 71 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))71 forall(T, allocator_t | allocator_c(T, allocator_t)) 72 72 void clear(vector(T, allocator_t)* this) 73 73 { … … 82 82 //Internal Helpers 83 83 84 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))84 forall(T, allocator_t | allocator_c(T, allocator_t)) 85 85 void copy_internal(vector(T, allocator_t)* this, vector(T, allocator_t)* other) 86 86 { … … 93 93 //------------------------------------------------------------------------------ 94 94 //Allocator 95 forall( otypeT)95 forall(T) 96 96 void ?{}(heap_allocator(T)& this) 97 97 { … … 100 100 } 101 101 102 forall( otypeT)102 forall(T) 103 103 void ?{}(heap_allocator(T)& this, heap_allocator(T) rhs) 104 104 { … … 107 107 } 108 108 109 forall( otypeT)109 forall(T) 110 110 heap_allocator(T) ?=?(heap_allocator(T)& this, heap_allocator(T) rhs) 111 111 { … … 115 115 } 116 116 117 forall( otypeT)117 forall(T) 118 118 void ^?{}(heap_allocator(T)& this) 119 119 { … … 121 121 } 122 122 123 forall( otypeT)123 forall(T) 124 124 inline void realloc_storage(heap_allocator(T)* this, size_t size) 125 125 { -
libcfa/src/containers/vector.hfa
rb6a8b31 rd95969a 20 20 //------------------------------------------------------------------------------ 21 21 //Allocator 22 forall( otypeT)22 forall(T) 23 23 struct heap_allocator 24 24 { … … 27 27 }; 28 28 29 forall( otypeT)29 forall(T) 30 30 void ?{}(heap_allocator(T)& this); 31 31 32 forall( otypeT)32 forall(T) 33 33 void ?{}(heap_allocator(T)& this, heap_allocator(T) rhs); 34 34 35 forall( otypeT)35 forall(T) 36 36 heap_allocator(T) ?=?(heap_allocator(T)& this, heap_allocator(T) rhs); 37 37 38 forall( otypeT)38 forall(T) 39 39 void ^?{}(heap_allocator(T)& this); 40 40 41 forall( otypeT)41 forall(T) 42 42 void realloc_storage(heap_allocator(T)* this, size_t size); 43 43 44 forall( otypeT)44 forall(T) 45 45 static inline T* data(heap_allocator(T)* this) 46 46 { … … 50 50 //------------------------------------------------------------------------------ 51 51 //Declaration 52 trait allocator_c( otype T, otypeallocator_t)52 trait allocator_c(T, allocator_t) 53 53 { 54 54 void realloc_storage(allocator_t*, size_t); … … 56 56 }; 57 57 58 forall( otype T, otypeallocator_t = heap_allocator(T) | allocator_c(T, allocator_t))58 forall(T, allocator_t = heap_allocator(T) | allocator_c(T, allocator_t)) 59 59 struct vector; 60 60 61 61 //------------------------------------------------------------------------------ 62 62 //Initialization 63 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))63 forall(T, allocator_t | allocator_c(T, allocator_t)) 64 64 void ?{}(vector(T, allocator_t)& this); 65 65 66 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))66 forall(T, allocator_t | allocator_c(T, allocator_t)) 67 67 void ?{}(vector(T, allocator_t)& this, vector(T, allocator_t) rhs); 68 68 69 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))69 forall(T, allocator_t | allocator_c(T, allocator_t)) 70 70 vector(T, allocator_t) ?=?(vector(T, allocator_t)& this, vector(T, allocator_t) rhs); 71 71 72 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))72 forall(T, allocator_t | allocator_c(T, allocator_t)) 73 73 void ^?{}(vector(T, allocator_t)& this); 74 74 75 forall( otype T, otypeallocator_t = heap_allocator(T) | allocator_c(T, allocator_t))75 forall(T, allocator_t = heap_allocator(T) | allocator_c(T, allocator_t)) 76 76 struct vector 77 77 { … … 82 82 //------------------------------------------------------------------------------ 83 83 //Capacity 84 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))84 forall(T, allocator_t | allocator_c(T, allocator_t)) 85 85 static inline bool empty(vector(T, allocator_t)* this) 86 86 { … … 88 88 } 89 89 90 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))90 forall(T, allocator_t | allocator_c(T, allocator_t)) 91 91 static inline size_t size(vector(T, allocator_t)* this) 92 92 { … … 94 94 } 95 95 96 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))96 forall(T, allocator_t | allocator_c(T, allocator_t)) 97 97 static inline void reserve(vector(T, allocator_t)* this, size_t size) 98 98 { … … 102 102 //------------------------------------------------------------------------------ 103 103 //Element access 104 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))104 forall(T, allocator_t | allocator_c(T, allocator_t)) 105 105 static inline T at(vector(T, allocator_t)* this, size_t index) 106 106 { … … 108 108 } 109 109 110 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))110 forall(T, allocator_t | allocator_c(T, allocator_t)) 111 111 static inline T ?[?](vector(T, allocator_t)* this, size_t index) 112 112 { … … 114 114 } 115 115 116 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))116 forall(T, allocator_t | allocator_c(T, allocator_t)) 117 117 static inline T front(vector(T, allocator_t)* this) 118 118 { … … 120 120 } 121 121 122 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))122 forall(T, allocator_t | allocator_c(T, allocator_t)) 123 123 static inline T back(vector(T, allocator_t)* this) 124 124 { … … 128 128 //------------------------------------------------------------------------------ 129 129 //Modifiers 130 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))130 forall(T, allocator_t | allocator_c(T, allocator_t)) 131 131 void push_back(vector(T, allocator_t)* this, T value); 132 132 133 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))133 forall(T, allocator_t | allocator_c(T, allocator_t)) 134 134 void pop_back(vector(T, allocator_t)* this); 135 135 136 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))136 forall(T, allocator_t | allocator_c(T, allocator_t)) 137 137 void clear(vector(T, allocator_t)* this); 138 138 139 139 //------------------------------------------------------------------------------ 140 140 //Iterators 141 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))141 forall(T, allocator_t | allocator_c(T, allocator_t)) 142 142 static inline T* begin(vector(T, allocator_t)* this) 143 143 { … … 145 145 } 146 146 147 // forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))147 // forall(T, allocator_t | allocator_c(T, allocator_t)) 148 148 // static inline const T* cbegin(const vector(T, allocator_t)* this) 149 149 // { … … 151 151 // } 152 152 153 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))153 forall(T, allocator_t | allocator_c(T, allocator_t)) 154 154 static inline T* end(vector(T, allocator_t)* this) 155 155 { … … 157 157 } 158 158 159 // forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))159 // forall(T, allocator_t | allocator_c(T, allocator_t)) 160 160 // static inline const T* cend(const vector(T, allocator_t)* this) 161 161 // { -
libcfa/src/exception.h
rb6a8b31 rd95969a 101 101 // implemented in the .c file either so they all have to be inline. 102 102 103 trait is_exception( dtype exceptT, dtype virtualT) {103 trait is_exception(exceptT &, virtualT &) { 104 104 /* The first field must be a pointer to a virtual table. 105 105 * That virtual table must be a decendent of the base exception virtual table. … … 109 109 }; 110 110 111 trait is_termination_exception( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT)) {111 trait is_termination_exception(exceptT &, virtualT & | is_exception(exceptT, virtualT)) { 112 112 void defaultTerminationHandler(exceptT &); 113 113 }; 114 114 115 trait is_resumption_exception( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT)) {115 trait is_resumption_exception(exceptT &, virtualT & | is_exception(exceptT, virtualT)) { 116 116 void defaultResumptionHandler(exceptT &); 117 117 }; 118 118 119 forall( dtype exceptT, dtype virtualT| is_termination_exception(exceptT, virtualT))119 forall(exceptT &, virtualT & | is_termination_exception(exceptT, virtualT)) 120 120 static inline void $throw(exceptT & except) { 121 121 __cfaehm_throw_terminate( … … 125 125 } 126 126 127 forall( dtype exceptT, dtype virtualT| is_resumption_exception(exceptT, virtualT))127 forall(exceptT &, virtualT & | is_resumption_exception(exceptT, virtualT)) 128 128 static inline void $throwResume(exceptT & except) { 129 129 __cfaehm_throw_resume( … … 133 133 } 134 134 135 forall( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT))135 forall(exceptT &, virtualT & | is_exception(exceptT, virtualT)) 136 136 static inline void cancel_stack(exceptT & except) __attribute__((noreturn)) { 137 137 __cfaehm_cancel_stack( (exception_t *)&except ); 138 138 } 139 139 140 forall( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT))140 forall(exceptT &, virtualT & | is_exception(exceptT, virtualT)) 141 141 static inline void defaultTerminationHandler(exceptT & except) { 142 142 return cancel_stack( except ); 143 143 } 144 144 145 forall( dtype exceptT, dtype virtualT| is_exception(exceptT, virtualT))145 forall(exceptT &, virtualT & | is_exception(exceptT, virtualT)) 146 146 static inline void defaultResumptionHandler(exceptT & except) { 147 147 throw except; -
libcfa/src/executor.cfa
rb6a8b31 rd95969a 7 7 #include <containers/list.hfa> 8 8 9 forall( dtype T| $dlistable(T, T) ) {9 forall( T & | $dlistable(T, T) ) { 10 10 monitor Buffer { // unbounded buffer 11 11 dlist( T, T ) queue; // unbounded list of work requests -
libcfa/src/gmp.hfa
rb6a8b31 rd95969a 255 255 256 256 // I/O 257 forall( dtype istype| istream( istype ) )257 forall( istype & | istream( istype ) ) 258 258 istype & ?|?( istype & is, Int & mp ) { 259 259 gmp_scanf( "%Zd", &mp ); … … 261 261 } // ?|? 262 262 263 forall( dtype ostype| ostream( ostype ) ) {263 forall( ostype & | ostream( ostype ) ) { 264 264 ostype & ?|?( ostype & os, Int mp ) { 265 265 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); -
libcfa/src/interpose.cfa
rb6a8b31 rd95969a 125 125 126 126 // Failure handler 127 __cfaabi_sigaction( SIGSEGV, sigHandler_segv, SA_SIGINFO | SA_ONSTACK ); 128 __cfaabi_sigaction( SIGBUS , sigHandler_segv, SA_SIGINFO | SA_ONSTACK ); 129 __cfaabi_sigaction( SIGILL , sigHandler_ill , SA_SIGINFO | SA_ONSTACK ); 130 __cfaabi_sigaction( SIGFPE , sigHandler_fpe , SA_SIGINFO | SA_ONSTACK ); 131 __cfaabi_sigaction( SIGTERM, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // one shot handler, return to default 132 __cfaabi_sigaction( SIGINT , sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); 133 __cfaabi_sigaction( SIGABRT, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); 134 __cfaabi_sigaction( SIGHUP , sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // terminal hangup 127 // internal errors 128 __cfaabi_sigaction( SIGSEGV, sigHandler_segv, SA_SIGINFO | SA_ONSTACK ); // Invalid memory reference (default: Core) 129 __cfaabi_sigaction( SIGBUS , sigHandler_segv, SA_SIGINFO | SA_ONSTACK ); // Bus error, bad memory access (default: Core) 130 __cfaabi_sigaction( SIGILL , sigHandler_ill , SA_SIGINFO | SA_ONSTACK ); // Illegal Instruction (default: Core) 131 __cfaabi_sigaction( SIGFPE , sigHandler_fpe , SA_SIGINFO | SA_ONSTACK ); // Floating-point exception (default: Core) 132 133 // handlers to outside errors 134 // reset in-case they insist and send it over and over 135 __cfaabi_sigaction( SIGTERM, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Termination signal (default: Term) 136 __cfaabi_sigaction( SIGINT , sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Interrupt from keyboard (default: Term) 137 __cfaabi_sigaction( SIGHUP , sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Hangup detected on controlling terminal or death of controlling process (default: Term) 138 __cfaabi_sigaction( SIGQUIT, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Quit from keyboard (default: Core) 139 __cfaabi_sigaction( SIGABRT, sigHandler_term, SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ); // Abort signal from abort(3) (default: Core) 135 140 } 136 141 } … … 163 168 } 164 169 165 void * kernel_abort( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) { return 0p; } 166 void kernel_abort_msg( void * data, char buffer[], int size) __attribute__(( __nothrow__, __leaf__, __weak__ )) {}167 // See concurrency/kernel.cfa for strong definition used in multi-processor mode. 168 int kernel_abort_lastframe( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) { return 4; }170 // See concurrency/kernel.cfa and concurrency/preemption.cfa for strong definition used in multi-processor mode. 171 void __kernel_abort_lock( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) {} 172 void __kernel_abort_msg( char buffer[], int size ) __attribute__(( __nothrow__, __leaf__, __weak__ )) {} 173 int __kernel_abort_lastframe( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) { return 4; } 169 174 170 175 enum { abort_text_size = 1024 }; … … 173 178 static void __cfaabi_backtrace( int start ) { 174 179 enum { Frames = 50, }; // maximum number of stack frames 175 int last = kernel_abort_lastframe(); // skip last N stack frames180 int last = __kernel_abort_lastframe(); // skip last N stack frames 176 181 177 182 void * array[Frames]; … … 220 225 } 221 226 222 static volatile int __abort_stage= 0;227 static volatile bool __abort_first = 0; 223 228 224 229 // Cannot forward va_list. 225 230 void __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 231 // Multiple threads can come here from multiple paths 232 // To make sure this is safe any concurrent/subsequent call to abort is redirected to libc-abort 233 bool first = ! __atomic_test_and_set( &__abort_first, __ATOMIC_SEQ_CST); 234 235 // Prevent preemption from kicking-in and messing with the abort 236 __kernel_abort_lock(); 237 238 // first to abort ? 239 if ( !first ) { 240 // We aren't the first to abort just let C handle it 241 signal( SIGABRT, SIG_DFL ); // restore default in case we came here through the function. 264 242 __cabi_libc.abort(); 265 266 // Loop so that we never return 267 } while(true); 243 } 244 245 int len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld) ", (long int)getpid() ); // use UNIX pid (versus getPid) 246 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 247 248 // print the cause of the error 249 assert( fmt ); 250 len = vsnprintf( abort_text, abort_text_size, fmt, args ); 251 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 252 253 // add optional newline if missing at the end of the format text 254 if ( fmt[strlen( fmt ) - 1] != '\n' ) { 255 __cfaabi_bits_write( STDERR_FILENO, "\n", 1 ); 256 } // if 257 258 // Give the kernel the chance to add some data in here 259 __kernel_abort_msg( abort_text, abort_text_size ); 260 261 // print stack trace in handler 262 __cfaabi_backtrace( signalAbort ? 4 : 2 ); 263 264 // Finally call abort 265 __cabi_libc.abort(); 266 268 267 } 269 268 -
libcfa/src/iostream.cfa
rb6a8b31 rd95969a 36 36 37 37 38 forall( dtype ostype| ostream( ostype ) ) {38 forall( ostype & | ostream( ostype ) ) { 39 39 ostype & ?|?( ostype & os, bool b ) { 40 40 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); … … 402 402 403 403 // tuples 404 forall( dtype ostype, otype T, ttype Params| writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) {404 forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) { 405 405 ostype & ?|?( ostype & os, T arg, Params rest ) { 406 406 (ostype &)(os | arg); // print first argument … … 421 421 422 422 // writes the range [begin, end) to the given stream 423 forall( dtype ostype, otype elt_type | writeable( elt_type, ostype ), otypeiterator_type | iterator( iterator_type, elt_type ) ) {423 forall( ostype &, elt_type | writeable( elt_type, ostype ), iterator_type | iterator( iterator_type, elt_type ) ) { 424 424 void write( iterator_type begin, iterator_type end, ostype & os ) { 425 425 void print( elt_type i ) { os | i; } … … 442 442 // Default prefix for non-decimal prints is 0b, 0, 0x. 443 443 #define IntegralFMTImpl( T, IFMTNP, IFMTP ) \ 444 forall( dtype ostype| ostream( ostype ) ) { \444 forall( ostype & | ostream( ostype ) ) { \ 445 445 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 446 446 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ … … 535 535 // Default prefix for non-decimal prints is 0b, 0, 0x. 536 536 #define IntegralFMTImpl128( T, SIGNED, CODE, IFMTNP, IFMTP ) \ 537 forall( dtype ostype| ostream( ostype ) ) \537 forall( ostype & | ostream( ostype ) ) \ 538 538 static void base10_128( ostype & os, _Ostream_Manip(T) f ) { \ 539 539 if ( f.val > UINT64_MAX ) { \ … … 552 552 } /* if */ \ 553 553 } /* base10_128 */ \ 554 forall( dtype ostype| ostream( ostype ) ) { \554 forall( ostype & | ostream( ostype ) ) { \ 555 555 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 556 556 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ … … 654 654 #if defined( __SIZEOF_INT128__ ) 655 655 // Default prefix for non-decimal prints is 0b, 0, 0x. 656 forall( dtype ostype| ostream( ostype ) )656 forall( ostype & | ostream( ostype ) ) 657 657 static 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 658 int wd = 1; // f.wd is never 0 because 0 implies left-pad … … 719 719 720 720 #define IntegralFMTImpl128( T ) \ 721 forall( dtype ostype| ostream( ostype ) ) { \721 forall( ostype & | ostream( ostype ) ) { \ 722 722 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 723 723 _Ostream_Manip(uint64_t) fmt; \ … … 767 767 768 768 #define FloatingPointFMTImpl( T, DFMTNP, DFMTP ) \ 769 forall( dtype ostype| ostream( ostype ) ) { \769 forall( ostype & | ostream( ostype ) ) { \ 770 770 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 771 771 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ … … 801 801 // *********************************** character *********************************** 802 802 803 forall( dtype ostype| ostream( ostype ) ) {803 forall( ostype & | ostream( ostype ) ) { 804 804 ostype & ?|?( ostype & os, _Ostream_Manip(char) f ) { 805 805 if ( f.base != 'c' ) { // bespoke binary/octal/hex format … … 834 834 // *********************************** C string *********************************** 835 835 836 forall( dtype ostype| ostream( ostype ) ) {836 forall( ostype & | ostream( ostype ) ) { 837 837 ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ) { 838 838 if ( ! f.val ) return os; // null pointer ? … … 882 882 883 883 884 forall( dtype istype| istream( istype ) ) {884 forall( istype & | istream( istype ) ) { 885 885 istype & ?|?( istype & is, bool & b ) { 886 886 char val[6]; … … 1048 1048 // *********************************** manipulators *********************************** 1049 1049 1050 forall( dtype istype| istream( istype ) )1050 forall( istype & | istream( istype ) ) 1051 1051 istype & ?|?( istype & is, _Istream_Cstr f ) { 1052 1052 // skip xxx … … 1083 1083 } // ?|? 1084 1084 1085 forall( dtype istype| istream( istype ) )1085 forall( istype & | istream( istype ) ) 1086 1086 istype & ?|?( istype & is, _Istream_Char f ) { 1087 1087 fmt( is, "%*c" ); // argument variable unused … … 1090 1090 1091 1091 #define InputFMTImpl( T, CODE ) \ 1092 forall( dtype istype| istream( istype ) ) \1092 forall( istype & | istream( istype ) ) \ 1093 1093 istype & ?|?( istype & is, _Istream_Manip(T) f ) { \ 1094 1094 enum { size = 16 }; \ … … 1119 1119 InputFMTImpl( long double, "Lf" ) 1120 1120 1121 forall( dtype istype| istream( istype ) )1121 forall( istype & | istream( istype ) ) 1122 1122 istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) { 1123 1123 float re, im; … … 1130 1130 } // ?|? 1131 1131 1132 forall( dtype istype| istream( istype ) )1132 forall( istype & | istream( istype ) ) 1133 1133 istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) { 1134 1134 double re, im; … … 1141 1141 } // ?|? 1142 1142 1143 forall( dtype istype| istream( istype ) )1143 forall( istype & | istream( istype ) ) 1144 1144 istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) { 1145 1145 long double re, im; -
libcfa/src/iostream.hfa
rb6a8b31 rd95969a 22 22 23 23 24 trait ostream( dtype ostype) {24 trait ostream( ostype & ) { 25 25 // private 26 26 bool $sepPrt( ostype & ); // get separator state (on/off) … … 56 56 }; // ostream 57 57 58 // trait writeable( otypeT ) {59 // forall( dtype ostype| ostream( ostype ) ) ostype & ?|?( ostype &, T );58 // trait writeable( T ) { 59 // forall( ostype & | ostream( ostype ) ) ostype & ?|?( ostype &, T ); 60 60 // }; // writeable 61 61 62 trait writeable( otype T, dtype ostype| ostream( ostype ) ) {62 trait writeable( T, ostype & | ostream( ostype ) ) { 63 63 ostype & ?|?( ostype &, T ); 64 64 }; // writeable … … 66 66 // implement writable for intrinsic types 67 67 68 forall( dtype ostype| ostream( ostype ) ) {68 forall( ostype & | ostream( ostype ) ) { 69 69 ostype & ?|?( ostype &, bool ); 70 70 void ?|?( ostype &, bool ); … … 140 140 141 141 // tuples 142 forall( dtype ostype, otype T, ttype Params| writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) {142 forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) { 143 143 ostype & ?|?( ostype & os, T arg, Params rest ); 144 144 void ?|?( ostype & os, T arg, Params rest ); … … 146 146 147 147 // writes the range [begin, end) to the given stream 148 forall( dtype ostype, otype elt_type | writeable( elt_type, ostype ), otypeiterator_type | iterator( iterator_type, elt_type ) ) {148 forall( ostype &, elt_type | writeable( elt_type, ostype ), iterator_type | iterator( iterator_type, elt_type ) ) { 149 149 void write( iterator_type begin, iterator_type end, ostype & os ); 150 150 void write_reverse( iterator_type begin, iterator_type end, ostype & os ); … … 153 153 // *********************************** manipulators *********************************** 154 154 155 forall( otypeT )155 forall( T ) 156 156 struct _Ostream_Manip { 157 157 T val; // polymorphic base-type … … 193 193 _Ostream_Manip(T) & sign( _Ostream_Manip(T) & fmt ) { fmt.flags.sign = true; return fmt; } \ 194 194 } /* distribution */ \ 195 forall( dtype ostype| ostream( ostype ) ) { \195 forall( ostype & | ostream( ostype ) ) { \ 196 196 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \ 197 197 void ?|?( ostype & os, _Ostream_Manip(T) f ); \ … … 234 234 _Ostream_Manip(T) & nodp( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \ 235 235 } /* distribution */ \ 236 forall( dtype ostype| ostream( ostype ) ) { \236 forall( ostype & | ostream( ostype ) ) { \ 237 237 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \ 238 238 void ?|?( ostype & os, _Ostream_Manip(T) f ); \ … … 254 254 _Ostream_Manip(char) & nobase( _Ostream_Manip(char) & fmt ) { fmt.flags.nobsdp = true; return fmt; } 255 255 } // distribution 256 forall( dtype ostype| ostream( ostype ) ) {256 forall( ostype & | ostream( ostype ) ) { 257 257 ostype & ?|?( ostype & os, _Ostream_Manip(char) f ); 258 258 void ?|?( ostype & os, _Ostream_Manip(char) f ); … … 272 272 _Ostream_Manip(const char *) & nobase( _Ostream_Manip(const char *) & fmt ) { fmt.flags.nobsdp = true; return fmt; } 273 273 } // distribution 274 forall( dtype ostype| ostream( ostype ) ) {274 forall( ostype & | ostream( ostype ) ) { 275 275 ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ); 276 276 void ?|?( ostype & os, _Ostream_Manip(const char *) f ); … … 281 281 282 282 283 trait istream( dtype istype) {283 trait istream( istype & ) { 284 284 void nlOn( istype & ); // read newline 285 285 void nlOff( istype & ); // scan newline … … 294 294 }; // istream 295 295 296 trait readable( otypeT ) {297 forall( dtype istype| istream( istype ) ) istype & ?|?( istype &, T );296 trait readable( T ) { 297 forall( istype & | istream( istype ) ) istype & ?|?( istype &, T ); 298 298 }; // readable 299 299 300 forall( dtype istype| istream( istype ) ) {300 forall( istype & | istream( istype ) ) { 301 301 istype & ?|?( istype &, bool & ); 302 302 … … 363 363 _Istream_Cstr & wdi( unsigned int w, _Istream_Cstr & fmt ) { fmt.wd = w; return fmt; } 364 364 } // distribution 365 forall( dtype istype| istream( istype ) ) istype & ?|?( istype & is, _Istream_Cstr f );365 forall( istype & | istream( istype ) ) istype & ?|?( istype & is, _Istream_Cstr f ); 366 366 367 367 struct _Istream_Char { … … 373 373 _Istream_Char & ignore( _Istream_Char & fmt ) { fmt.ignore = true; return fmt; } 374 374 } // distribution 375 forall( dtype istype| istream( istype ) ) istype & ?|?( istype & is, _Istream_Char f );376 377 forall( dtype T| sized( T ) )375 forall( istype & | istream( istype ) ) istype & ?|?( istype & is, _Istream_Char f ); 376 377 forall( T & | sized( T ) ) 378 378 struct _Istream_Manip { 379 379 T & val; // polymorphic base-type … … 389 389 _Istream_Manip(T) & wdi( unsigned int w, _Istream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \ 390 390 } /* distribution */ \ 391 forall( dtype istype| istream( istype ) ) { \391 forall( istype & | istream( istype ) ) { \ 392 392 istype & ?|?( istype & is, _Istream_Manip(T) f ); \ 393 393 } // ?|? … … 418 418 #include <time_t.hfa> // Duration (constructors) / Time (constructors) 419 419 420 forall( dtype ostype| ostream( ostype ) ) {420 forall( ostype & | ostream( ostype ) ) { 421 421 ostype & ?|?( ostype & os, Duration dur ); 422 422 void ?|?( ostype & os, Duration dur ); -
libcfa/src/iterator.cfa
rb6a8b31 rd95969a 16 16 #include "iterator.hfa" 17 17 18 forall( otype iterator_type, otypeelt_type | iterator( iterator_type, elt_type ) )18 forall( iterator_type, elt_type | iterator( iterator_type, elt_type ) ) 19 19 void for_each( iterator_type begin, iterator_type end, void (* func)( elt_type ) ) { 20 20 for ( iterator_type i = begin; i != end; ++i ) { … … 23 23 } // for_each 24 24 25 forall( otype iterator_type, otypeelt_type | iterator( iterator_type, elt_type ) )25 forall( iterator_type, elt_type | iterator( iterator_type, elt_type ) ) 26 26 void for_each_reverse( iterator_type begin, iterator_type end, void (* func)( elt_type ) ) { 27 27 for ( iterator_type i = end; i != begin; ) { -
libcfa/src/iterator.hfa
rb6a8b31 rd95969a 17 17 18 18 // An iterator can be used to traverse a data structure. 19 trait iterator( otype iterator_type, otypeelt_type ) {19 trait iterator( iterator_type, elt_type ) { 20 20 // point to the next element 21 21 // iterator_type ?++( iterator_type & ); … … 31 31 }; 32 32 33 trait iterator_for( otype iterator_type, otype collection_type, otypeelt_type | iterator( iterator_type, elt_type ) ) {33 trait iterator_for( iterator_type, collection_type, elt_type | iterator( iterator_type, elt_type ) ) { 34 34 // [ iterator_type begin, iterator_type end ] get_iterators( collection_type ); 35 35 iterator_type begin( collection_type ); … … 37 37 }; 38 38 39 forall( otype iterator_type, otypeelt_type | iterator( iterator_type, elt_type ) )39 forall( iterator_type, elt_type | iterator( iterator_type, elt_type ) ) 40 40 void for_each( iterator_type begin, iterator_type end, void (* func)( elt_type ) ); 41 41 42 forall( otype iterator_type, otypeelt_type | iterator( iterator_type, elt_type ) )42 forall( iterator_type, elt_type | iterator( iterator_type, elt_type ) ) 43 43 void for_each_reverse( iterator_type begin, iterator_type end, void (* func)( elt_type ) ); 44 44 -
libcfa/src/math.hfa
rb6a8b31 rd95969a 286 286 unsigned long long int floor( unsigned long long int n, unsigned long long int align ) { return n / align * align; } 287 287 288 // forall( otypeT | { T ?/?( T, T ); T ?*?( T, T ); } )288 // forall( T | { T ?/?( T, T ); T ?*?( T, T ); } ) 289 289 // T floor( T n, T align ) { return n / align * align; } 290 290 … … 300 300 unsigned long long int ceiling_div( unsigned long long int n, unsigned long long int align ) { return (n + (align - 1)) / align; } 301 301 302 // forall( otypeT | { T ?+?( T, T ); T ?-?( T, T ); T ?%?( T, T ); } )302 // forall( T | { T ?+?( T, T ); T ?-?( T, T ); T ?%?( T, T ); } ) 303 303 // T ceiling_div( T n, T align ) { verify( is_pow2( align ) );return (n + (align - 1)) / align; } 304 304 … … 315 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 316 317 // forall( otypeT | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T ); T ?/?( T, T ); } )317 // forall( T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T ); T ?/?( T, T ); } ) 318 318 // T ceiling( T n, T align ) { return return floor( n + (n % align != 0 ? align - 1 : 0), align ); *} 319 319 … … 414 414 415 415 static inline { 416 forall( otypeT | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } )416 forall( T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } ) 417 417 T lerp( T x, T y, T a ) { return x * ((T){1} - a) + y * a; } 418 418 419 forall( otypeT | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } )419 forall( T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } ) 420 420 T step( T edge, T x ) { return x < edge ? (T){0} : (T){1}; } 421 421 422 forall( otypeT | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } )422 forall( T | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } ) 423 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 424 } // distribution -
libcfa/src/memory.cfa
rb6a8b31 rd95969a 18 18 19 19 // Internal data object. 20 forall( dtype T | sized(T), ttype Args| { void ?{}(T &, Args); })20 forall(T & | sized(T), Args... | { void ?{}(T &, Args); }) 21 21 void ?{}(counter_data(T) & this, Args args) { 22 22 (this.counter){1}; … … 24 24 } 25 25 26 forall( dtype T| sized(T) | { void ^?{}(T &); })26 forall(T & | sized(T) | { void ^?{}(T &); }) 27 27 void ^?{}(counter_data(T) & this) { 28 28 assert(0 == this.counter); … … 31 31 32 32 // This is one of many pointers keeping this alive. 33 forall( dtype T| sized(T))33 forall(T & | sized(T)) 34 34 void ?{}(counter_ptr(T) & this) { 35 35 this.data = 0p; 36 36 } 37 37 38 forall( dtype T| sized(T))38 forall(T & | sized(T)) 39 39 void ?{}(counter_ptr(T) & this, zero_t) { 40 40 this.data = 0p; 41 41 } 42 42 43 forall( dtype T| sized(T) | { void ^?{}(T &); })43 forall(T & | sized(T) | { void ^?{}(T &); }) 44 44 static void internal_decrement(counter_ptr(T) & this) { 45 45 if (this.data && 0 == --this.data->counter) { … … 48 48 } 49 49 50 forall( dtype T| sized(T))50 forall(T & | sized(T)) 51 51 static void internal_copy(counter_ptr(T) & this, counter_ptr(T) & that) { 52 52 this.data = that.data; … … 56 56 } 57 57 58 forall( dtype T| sized(T) | { void ^?{}(T &); })58 forall(T & | sized(T) | { void ^?{}(T &); }) 59 59 void ?{}(counter_ptr(T) & this, counter_ptr(T) that) { 60 60 // `that` is a copy but it should have neither a constructor … … 64 64 } 65 65 66 forall( dtype T | sized(T), ttype Args| { void ?{}(T&, Args); })66 forall(T & | sized(T), Args... | { void ?{}(T&, Args); }) 67 67 void ?{}(counter_ptr(T) & this, Args args) { 68 68 this.data = (counter_data(T)*)new(args); 69 69 } 70 70 71 forall( dtype T| sized(T) | { void ^?{}(T &); })71 forall(T & | sized(T) | { void ^?{}(T &); }) 72 72 void ^?{}(counter_ptr(T) & this) { 73 73 internal_decrement(this); 74 74 } 75 75 76 forall( dtype T| sized(T))76 forall(T & | sized(T)) 77 77 T & *?(counter_ptr(T) & this) { 78 78 return *((this.data) ? &this.data->object : 0p); 79 79 } 80 80 81 forall( dtype T| sized(T) | { void ^?{}(T &); })81 forall(T & | sized(T) | { void ^?{}(T &); }) 82 82 void ?=?(counter_ptr(T) & this, counter_ptr(T) that) { 83 83 if (this.data != that.data) { … … 87 87 } 88 88 89 forall( dtype T| sized(T) | { void ^?{}(T &); })89 forall(T & | sized(T) | { void ^?{}(T &); }) 90 90 void ?=?(counter_ptr(T) & this, zero_t) { 91 91 internal_decrement(this); … … 93 93 } 94 94 95 forall( dtype T| sized(T))95 forall(T & | sized(T)) 96 96 int ?==?(counter_ptr(T) const & this, counter_ptr(T) const & that) { 97 97 return this.data == that.data; 98 98 } 99 99 100 forall( dtype T| sized(T))100 forall(T & | sized(T)) 101 101 int ?!=?(counter_ptr(T) const & this, counter_ptr(T) const & that) { 102 102 return !?==?(this, that); 103 103 } 104 104 105 forall( dtype T| sized(T))105 forall(T & | sized(T)) 106 106 int ?==?(counter_ptr(T) const & this, zero_t) { 107 107 return this.data == 0; 108 108 } 109 109 110 forall( dtype T| sized(T))110 forall(T & | sized(T)) 111 111 int ?!=?(counter_ptr(T) const & this, zero_t) { 112 112 return !?==?(this, (zero_t)0); … … 114 114 115 115 // This is the only pointer that keeps this alive. 116 forall( dtype T)116 forall(T &) 117 117 void ?{}(unique_ptr(T) & this) { 118 118 this.data = 0p; 119 119 } 120 120 121 forall( dtype T)121 forall(T &) 122 122 void ?{}(unique_ptr(T) & this, zero_t) { 123 123 this.data = 0p; 124 124 } 125 125 126 forall( dtype T | sized(T), ttype Args| { void ?{}(T &, Args); })126 forall(T & | sized(T), Args... | { void ?{}(T &, Args); }) 127 127 void ?{}(unique_ptr(T) & this, Args args) { 128 128 this.data = (T *)new(args); 129 129 } 130 130 131 forall( dtype T| { void ^?{}(T &); })131 forall(T & | { void ^?{}(T &); }) 132 132 void ^?{}(unique_ptr(T) & this) { 133 133 delete(this.data); 134 134 } 135 135 136 forall( dtype T)136 forall(T &) 137 137 T & *?(unique_ptr(T) & this) { 138 138 return *this.data; 139 139 } 140 140 141 forall( dtype T| { void ^?{}(T &); })141 forall(T & | { void ^?{}(T &); }) 142 142 void ?=?(unique_ptr(T) & this, zero_t) { 143 143 delete(this.data); … … 145 145 } 146 146 147 forall( dtype T| { void ^?{}(T &); })147 forall(T & | { void ^?{}(T &); }) 148 148 void move(unique_ptr(T) & this, unique_ptr(T) & that) { 149 149 delete(this.data); … … 152 152 } 153 153 154 forall( dtype T)154 forall(T &) 155 155 int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that) { 156 156 return this.data == that.data; 157 157 } 158 158 159 forall( dtype T)159 forall(T &) 160 160 int ?!=?(unique_ptr(T) const & this, unique_ptr(T) const & that) { 161 161 return !?==?(this, that); 162 162 } 163 163 164 forall( dtype T)164 forall(T &) 165 165 int ?==?(unique_ptr(T) const & this, zero_t) { 166 166 return this.data == 0; 167 167 } 168 168 169 forall( dtype T)169 forall(T &) 170 170 int ?!=?(unique_ptr(T) const & this, zero_t) { 171 171 return !?==?(this, (zero_t)0); -
libcfa/src/memory.hfa
rb6a8b31 rd95969a 17 17 18 18 // Internal data object. 19 forall( dtype T| sized(T)) {19 forall(T & | sized(T)) { 20 20 struct counter_data { 21 21 unsigned int counter; … … 23 23 }; 24 24 25 forall( ttype Args| { void ?{}(T &, Args); })25 forall(Args... | { void ?{}(T &, Args); }) 26 26 void ?{}(counter_data(T) & this, Args args); 27 27 … … 31 31 32 32 // This is one of many pointers keeping this alive. 33 forall( dtype T| sized(T)) {33 forall(T & | sized(T)) { 34 34 struct counter_ptr { 35 35 counter_data(T) * data; … … 40 40 forall( | { void ^?{}(T &); }) 41 41 void ?{}(counter_ptr(T) & this, counter_ptr(T) that); 42 forall( ttype Args| { void ?{}(T&, Args); })42 forall(Args... | { void ?{}(T&, Args); }) 43 43 void ?{}(counter_ptr(T) & this, Args args); 44 44 … … 60 60 61 61 // This is the only pointer that keeps this alive. 62 forall( dtype T) {62 forall(T &) { 63 63 struct unique_ptr { 64 64 T * data; … … 68 68 void ?{}(unique_ptr(T) & this, zero_t); 69 69 void ?{}(unique_ptr(T) & this, unique_ptr(T) that) = void; 70 forall( | sized(T), ttype Args| { void ?{}(T &, Args); })70 forall( | sized(T), Args... | { void ?{}(T &, Args); }) 71 71 void ?{}(unique_ptr(T) & this, Args args); 72 72 -
libcfa/src/parseargs.cfa
rb6a8b31 rd95969a 30 30 31 31 static void usage(char * cmd, cfa_option options[], size_t opt_count, const char * usage, FILE * out) __attribute__ ((noreturn)); 32 32 //----------------------------------------------------------------------------- 33 // checking 34 static void check_args(cfa_option options[], size_t opt_count) { 35 for(i; opt_count) { 36 for(j; opt_count) { 37 if(i == j) continue; 38 39 if( options[i].short_name != '\0' 40 && options[i].short_name == options[j].short_name) 41 abort("Parse Args error: two options have short name '%c' (%zu & %zu)", options[i].short_name, i, j); 42 43 if(0 == strcmp(options[i].long_name, options[j].long_name)) abort("Parse Args error: two options have long name '%s' (%zu & %zu)", options[i].long_name, i, j); 44 } 45 } 46 } 47 48 49 //----------------------------------------------------------------------------- 50 // Parsing args 33 51 void parse_args( cfa_option options[], size_t opt_count, const char * usage, char ** & left ) { 34 52 if( 0p != &cfa_args_argc ) { … … 41 59 } 42 60 43 //-----------------------------------------------------------------------------44 // getopt_long wrapping45 61 void parse_args( 46 62 int argc, … … 51 67 char ** & left 52 68 ) { 69 check_args(options, opt_count); 70 71 int maxv = 'h'; 72 char optstring[opt_count * 3] = { '\0' }; 73 { 74 int idx = 0; 75 for(i; opt_count) { 76 if (options[i].short_name) { 77 maxv = max(options[i].short_name, maxv); 78 optstring[idx] = options[i].short_name; 79 idx++; 80 if( ((intptr_t)options[i].parse) != ((intptr_t)parse_settrue) 81 && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) { 82 optstring[idx] = ':'; 83 idx++; 84 } 85 } 86 } 87 optstring[idx+0] = 'h'; 88 optstring[idx+1] = '\0'; 89 } 90 53 91 struct option optarr[opt_count + 2]; 54 92 { … … 56 94 for(i; opt_count) { 57 95 if(options[i].long_name) { 96 options[i].val = (options[i].short_name != '\0') ? ((int)options[i].short_name) : ++maxv; 58 97 optarr[idx].name = options[i].long_name; 59 98 optarr[idx].flag = 0p; 60 optarr[idx].val = options[i]. short_name;99 optarr[idx].val = options[i].val; 61 100 if( ((intptr_t)options[i].parse) == ((intptr_t)parse_settrue) 62 101 || ((intptr_t)options[i].parse) == ((intptr_t)parse_setfalse) ) { … … 70 109 optarr[idx+0].[name, has_arg, flag, val] = ["help", no_argument, 0, 'h']; 71 110 optarr[idx+1].[name, has_arg, flag, val] = [0, no_argument, 0, 0]; 72 }73 74 char optstring[opt_count * 3] = { '\0' };75 {76 int idx = 0;77 for(i; opt_count) {78 optstring[idx] = options[i].short_name;79 idx++;80 if( ((intptr_t)options[i].parse) != ((intptr_t)parse_settrue)81 && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) {82 optstring[idx] = ':';83 idx++;84 }85 }86 optstring[idx+0] = 'h';87 optstring[idx+1] = '\0';88 111 } 89 112 … … 103 126 default: 104 127 for(i; opt_count) { 105 if(opt == options[i]. short_name) {128 if(opt == options[i].val) { 106 129 const char * arg = optarg ? optarg : ""; 107 130 if( arg[0] == '=' ) { arg++; } … … 125 148 if(hwidth <= 0) hwidth = max; 126 149 127 fprintf(out, " -%c, --%-*s %.*s\n", sn, width, ln, hwidth, help); 150 char sname[4] = { ' ', ' ', ' ', '\0' }; 151 if(sn != '\0') { 152 sname[0] = '-'; 153 sname[1] = sn; 154 sname[2] = ','; 155 } 156 157 fprintf(out, " %s --%-*s %.*s\n", sname, width, ln, hwidth, help); 128 158 for() { 129 159 help += min(strlen(help), hwidth); -
libcfa/src/parseargs.hfa
rb6a8b31 rd95969a 2 2 3 3 struct cfa_option { 4 int val; // reserved 4 5 char short_name; 5 6 const char * long_name; … … 13 14 static inline void ?{}( cfa_option & this ) {} 14 15 15 forall( dtype T| { bool parse(const char *, T & ); })16 forall(T & | { bool parse(const char *, T & ); }) 16 17 static inline void ?{}( cfa_option & this, char short_name, const char * long_name, const char * help, T & variable ) { 18 this.val = 0; 17 19 this.short_name = short_name; 18 20 this.long_name = long_name; … … 22 24 } 23 25 24 forall( dtype T)26 forall(T &) 25 27 static inline void ?{}( cfa_option & this, char short_name, const char * long_name, const char * help, T & variable, bool (*parse)(const char *, T & )) { 28 this.val = 0; 26 29 this.short_name = short_name; 27 30 this.long_name = long_name; -
libcfa/src/rational.cfa
rb6a8b31 rd95969a 18 18 #include "stdlib.hfa" 19 19 20 forall( otypeRationalImpl | arithmetic( RationalImpl ) ) {20 forall( RationalImpl | arithmetic( RationalImpl ) ) { 21 21 // helper routines 22 22 … … 159 159 // I/O 160 160 161 forall( dtype istype| istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } )161 forall( istype & | istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } ) 162 162 istype & ?|?( istype & is, Rational(RationalImpl) & r ) { 163 163 is | r.numerator | r.denominator; … … 168 168 } // ?|? 169 169 170 forall( dtype ostype| ostream( ostype ) | { ostype & ?|?( ostype &, RationalImpl ); } ) {170 forall( ostype & | ostream( ostype ) | { ostype & ?|?( ostype &, RationalImpl ); } ) { 171 171 ostype & ?|?( ostype & os, Rational(RationalImpl) r ) { 172 172 return os | r.numerator | '/' | r.denominator; … … 179 179 } // distribution 180 180 181 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { RationalImpl ?\?( RationalImpl, unsigned long ); } )181 forall( RationalImpl | arithmetic( RationalImpl ) | { RationalImpl ?\?( RationalImpl, unsigned long ); } ) 182 182 Rational(RationalImpl) ?\?( Rational(RationalImpl) x, long int y ) { 183 183 if ( y < 0 ) { … … 190 190 // conversion 191 191 192 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } )192 forall( RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } ) 193 193 double widen( Rational(RationalImpl) r ) { 194 194 return convert( r.numerator ) / convert( r.denominator ); 195 195 } // widen 196 196 197 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); RationalImpl convert( double ); } )197 forall( RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); RationalImpl convert( double ); } ) 198 198 Rational(RationalImpl) narrow( double f, RationalImpl md ) { 199 199 // http://www.ics.uci.edu/~eppstein/numth/frap.c -
libcfa/src/rational.hfa
rb6a8b31 rd95969a 20 20 #include "iostream.hfa" 21 21 22 trait scalar( otypeT ) {22 trait scalar( T ) { 23 23 }; 24 24 25 trait arithmetic( otypeT | scalar( T ) ) {25 trait arithmetic( T | scalar( T ) ) { 26 26 int !?( T ); 27 27 int ?==?( T, T ); … … 46 46 // implementation 47 47 48 forall( otypeRationalImpl | arithmetic( RationalImpl ) ) {48 forall( RationalImpl | arithmetic( RationalImpl ) ) { 49 49 struct Rational { 50 50 RationalImpl numerator, denominator; // invariant: denominator > 0 … … 89 89 90 90 // I/O 91 forall( dtype istype| istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } )91 forall( istype & | istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } ) 92 92 istype & ?|?( istype &, Rational(RationalImpl) & ); 93 93 94 forall( dtype ostype| ostream( ostype ) | { ostype & ?|?( ostype &, RationalImpl ); } ) {94 forall( ostype & | ostream( ostype ) | { ostype & ?|?( ostype &, RationalImpl ); } ) { 95 95 ostype & ?|?( ostype &, Rational(RationalImpl) ); 96 96 void ?|?( ostype &, Rational(RationalImpl) ); … … 98 98 } // distribution 99 99 100 forall( otypeRationalImpl | arithmetic( RationalImpl ) |{RationalImpl ?\?( RationalImpl, unsigned long );} )100 forall( RationalImpl | arithmetic( RationalImpl ) |{RationalImpl ?\?( RationalImpl, unsigned long );} ) 101 101 Rational(RationalImpl) ?\?( Rational(RationalImpl) x, long int y ); 102 102 103 103 // conversion 104 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } )104 forall( RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } ) 105 105 double widen( Rational(RationalImpl) r ); 106 forall( otypeRationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); RationalImpl convert( double );} )106 forall( RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); RationalImpl convert( double );} ) 107 107 Rational(RationalImpl) narrow( double f, RationalImpl md ); 108 108 -
libcfa/src/stdlib.cfa
rb6a8b31 rd95969a 28 28 // Cforall allocation/deallocation and constructor/destructor, array types 29 29 30 forall( dtype T | sized(T), ttype TT| { void ?{}( T &, TT ); } )30 forall( T & | sized(T), TT... | { void ?{}( T &, TT ); } ) 31 31 T * anew( size_t dim, TT p ) { 32 32 T * arr = alloc( dim ); … … 37 37 } // anew 38 38 39 forall( dtype T| sized(T) | { void ^?{}( T & ); } )39 forall( T & | sized(T) | { void ^?{}( T & ); } ) 40 40 void adelete( T arr[] ) { 41 41 if ( arr ) { // ignore null … … 48 48 } // adelete 49 49 50 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT| { void adelete( TT ); } )50 forall( T & | sized(T) | { void ^?{}( T & ); }, TT... | { void adelete( TT ); } ) 51 51 void adelete( T arr[], TT rest ) { 52 52 if ( arr ) { // ignore null … … 97 97 //--------------------------------------- 98 98 99 forall( otypeE | { int ?<?( E, E ); } ) {99 forall( E | { int ?<?( E, E ); } ) { 100 100 E * bsearch( E key, const E * vals, size_t dim ) { 101 101 int cmp( const void * t1, const void * t2 ) { … … 156 156 157 157 158 forall( otype K, otypeE | { int ?<?( K, K ); K getKey( const E & ); } ) {158 forall( K, E | { int ?<?( K, K ); K getKey( const E & ); } ) { 159 159 E * bsearch( K key, const E * vals, size_t dim ) { 160 160 int cmp( const void * t1, const void * t2 ) { -
libcfa/src/stdlib.hfa
rb6a8b31 rd95969a 10 10 // Created On : Thu Jan 28 17:12:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Dec 12 13:52:34 202013 // Update Count : 5 3612 // Last Modified On : Thu Jan 21 22:02:13 2021 13 // Update Count : 574 14 14 // 15 15 … … 48 48 else return (T *)alignment( _Alignof(T), dim, sizeof(T) ) 49 49 50 static inline forall( dtype T| sized(T) ) {50 static inline forall( T & | sized(T) ) { 51 51 // CFA safe equivalents, i.e., implicit size specification 52 52 … … 108 108 109 109 1. Replace the current forall-block that contains defintions of S_fill and S_realloc with following: 110 forall( dtype T| sized(T) ) {110 forall( T & | sized(T) ) { 111 111 union U_fill { char c; T * a; T t; }; 112 112 struct S_fill { char tag; U_fill(T) fill; }; … … 151 151 typedef struct S_resize { inline void *; } T_resize; 152 152 153 forall( dtype T) {153 forall( T & ) { 154 154 struct S_fill { char tag; char c; size_t size; T * at; char t[50]; }; 155 155 struct S_realloc { inline T *; }; … … 159 159 static inline T_resize ?`resize ( void * a ) { return (T_resize){a}; } 160 160 161 static inline forall( dtype T| sized(T) ) {161 static inline forall( T & | sized(T) ) { 162 162 S_fill(T) ?`fill ( T t ) { 163 163 S_fill(T) ret = { 't' }; 164 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); } 165 if ( size > sizeof(ret.t) ) { 166 abort( "ERROR: const object of size greater than 50 bytes given for dynamic memory fill\n" ); 167 } // if 166 168 memcpy( &ret.t, &t, size ); 167 169 return ret; … … 173 175 S_realloc(T) ?`realloc ( T * a ) { return (S_realloc(T)){a}; } 174 176 175 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill ) {177 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill ) { 176 178 T * ptr = NULL; 177 179 size_t size = sizeof(T); … … 181 183 ptr = (T*) (void *) resize( (void *)Resize, Align, Dim * size ); 182 184 } 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 if ( Fill.tag != '0' ) copy_end = min(malloc_size( Realloc ), Dim * size ); 186 ptr = (T *) (void *) realloc( (void *)Realloc, Align, Dim * size ); 185 187 } else { 186 ptr = (T *) (void *) memalign( Align, Dim * size );187 } 188 189 if (Fill.tag == 'c') {188 ptr = (T *) (void *) memalign( Align, Dim * size ); 189 } 190 191 if ( Fill.tag == 'c' ) { 190 192 memset( (char *)ptr + copy_end, (int)Fill.c, Dim * size - copy_end ); 191 } else if (Fill.tag == 't') {193 } else if ( Fill.tag == 't' ) { 192 194 for ( int i = copy_end; i < Dim * size; i += size ) { 195 #pragma GCC diagnostic push 196 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 197 assert( size <= sizeof(Fill.t) ); 193 198 memcpy( (char *)ptr + i, &Fill.t, size ); 199 #pragma GCC diagnostic pop 194 200 } 195 } else if (Fill.tag == 'a') {201 } else if ( Fill.tag == 'a' ) { 196 202 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 } 203 } else if ( Fill.tag == 'T' ) { 204 memcpy( (char *)ptr + copy_end, Fill.at, Dim * size ); 201 205 } 202 206 … … 204 208 } // $alloc_internal 205 209 206 forall( ttype TT| { T * $alloc_internal( void *, T *, size_t, size_t, S_fill(T), TT ); } ) {210 forall( TT... | { T * $alloc_internal( void *, T *, size_t, size_t, S_fill(T), TT ); } ) { 207 211 208 212 T * $alloc_internal( void * , T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill, T_resize Resize, TT rest) { … … 233 237 } // distribution T 234 238 235 static inline forall( dtype T| sized(T) ) {239 static inline forall( T & | sized(T) ) { 236 240 // CFA safe initialization/copy, i.e., implicit size specification, non-array types 237 241 T * memset( T * dest, char fill ) { … … 254 258 255 259 // CFA deallocation for multiple objects 256 static inline forall( dtype T) // FIX ME, problems with 0p in list260 static inline forall( T & ) // FIX ME, problems with 0p in list 257 261 void free( T * ptr ) { 258 262 free( (void *)ptr ); // C free 259 263 } // free 260 static inline forall( dtype T, ttype TT| { void free( TT ); } )264 static inline forall( T &, TT... | { void free( TT ); } ) 261 265 void free( T * ptr, TT rest ) { 262 266 free( ptr ); … … 265 269 266 270 // CFA allocation/deallocation and constructor/destructor, non-array types 267 static inline forall( dtype T | sized(T), ttype TT| { void ?{}( T &, TT ); } )271 static inline forall( T & | sized(T), TT... | { void ?{}( T &, TT ); } ) 268 272 T * new( TT p ) { 269 return &(*(T *)malloc()){ p }; // run constructor273 return &(*(T *)malloc()){ p }; // run constructor 270 274 } // new 271 275 272 static inline forall( dtype T| { void ^?{}( T & ); } )276 static inline forall( T & | { void ^?{}( T & ); } ) 273 277 void delete( T * ptr ) { 274 278 // special case for 0-sized object => always call destructor … … 278 282 free( ptr ); // always call free 279 283 } // delete 280 static inline forall( dtype T, ttype TT| { void ^?{}( T & ); void delete( TT ); } )284 static inline forall( T &, TT... | { void ^?{}( T & ); void delete( TT ); } ) 281 285 void delete( T * ptr, TT rest ) { 282 286 delete( ptr ); … … 285 289 286 290 // CFA allocation/deallocation and constructor/destructor, array types 287 forall( dtype T | sized(T), ttype TT| { void ?{}( T &, TT ); } ) T * anew( size_t dim, TT p );288 forall( dtype T| sized(T) | { void ^?{}( T & ); } ) void adelete( T arr[] );289 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT| { void adelete( TT ); } ) void adelete( T arr[], TT rest );291 forall( T & | sized(T), TT... | { void ?{}( T &, TT ); } ) T * anew( size_t dim, TT p ); 292 forall( T & | sized(T) | { void ^?{}( T & ); } ) void adelete( T arr[] ); 293 forall( T & | sized(T) | { void ^?{}( T & ); }, TT... | { void adelete( TT ); } ) void adelete( T arr[], TT rest ); 290 294 291 295 //--------------------------------------- … … 327 331 //--------------------------------------- 328 332 329 forall( otypeE | { int ?<?( E, E ); } ) {333 forall( E | { int ?<?( E, E ); } ) { 330 334 E * bsearch( E key, const E * vals, size_t dim ); 331 335 size_t bsearch( E key, const E * vals, size_t dim ); … … 336 340 } // distribution 337 341 338 forall( otype K, otypeE | { int ?<?( K, K ); K getKey( const E & ); } ) {342 forall( K, E | { int ?<?( K, K ); K getKey( const E & ); } ) { 339 343 E * bsearch( K key, const E * vals, size_t dim ); 340 344 size_t bsearch( K key, const E * vals, size_t dim ); … … 345 349 } // distribution 346 350 347 forall( otypeE | { int ?<?( E, E ); } ) {351 forall( E | { int ?<?( E, E ); } ) { 348 352 void qsort( E * vals, size_t dim ); 349 353 } // distribution -
libcfa/src/time.cfa
rb6a8b31 rd95969a 31 31 32 32 33 forall( dtype ostype| ostream( ostype ) ) {33 forall( ostype & | ostream( ostype ) ) { 34 34 ostype & ?|?( ostype & os, Duration dur ) with( dur ) { 35 35 (ostype &)(os | tn / TIMEGRAN); // print seconds … … 136 136 } // strftime 137 137 138 forall( dtype ostype| ostream( ostype ) ) {138 forall( ostype & | ostream( ostype ) ) { 139 139 ostype & ?|?( ostype & os, Time time ) with( time ) { 140 140 char buf[32]; // at least 26 -
libcfa/src/vec/vec.hfa
rb6a8b31 rd95969a 18 18 #include <math.hfa> 19 19 20 trait fromint( otypeT) {20 trait fromint(T) { 21 21 void ?{}(T&, int); 22 22 }; 23 trait zeroinit( otypeT) {23 trait zeroinit(T) { 24 24 void ?{}(T&, zero_t); 25 25 }; 26 trait zero_assign( otypeT) {26 trait zero_assign(T) { 27 27 T ?=?(T&, zero_t); 28 28 }; 29 trait subtract( otypeT) {29 trait subtract(T) { 30 30 T ?-?(T, T); 31 31 }; 32 trait negate( otypeT) {32 trait negate(T) { 33 33 T -?(T); 34 34 }; 35 trait add( otypeT) {35 trait add(T) { 36 36 T ?+?(T, T); 37 37 }; 38 trait multiply( otypeT) {38 trait multiply(T) { 39 39 T ?*?(T, T); 40 40 }; 41 trait divide( otypeT) {41 trait divide(T) { 42 42 T ?/?(T, T); 43 43 }; 44 trait lessthan( otypeT) {44 trait lessthan(T) { 45 45 int ?<?(T, T); 46 46 }; 47 trait equality( otypeT) {47 trait equality(T) { 48 48 int ?==?(T, T); 49 49 }; 50 trait sqrt( otypeT) {50 trait sqrt(T) { 51 51 T sqrt(T); 52 52 }; … … 68 68 } 69 69 70 trait dottable( otype V, otypeT) {70 trait dottable(V, T) { 71 71 T dot(V, V); 72 72 }; … … 74 74 static inline { 75 75 76 forall( otype T | sqrt(T), otypeV | dottable(V, T))76 forall(T | sqrt(T), V | dottable(V, T)) 77 77 T length(V v) { 78 78 return sqrt(dot(v, v)); 79 79 } 80 80 81 forall( otype T, otypeV | dottable(V, T))81 forall(T, V | dottable(V, T)) 82 82 T length_squared(V v) { 83 83 return dot(v, v); 84 84 } 85 85 86 forall( otype T, otypeV | { T length(V); } | subtract(V))86 forall(T, V | { T length(V); } | subtract(V)) 87 87 T distance(V v1, V v2) { 88 88 return length(v1 - v2); 89 89 } 90 90 91 forall( otype T, otypeV | { T length(V); V ?/?(V, T); })91 forall(T, V | { T length(V); V ?/?(V, T); }) 92 92 V normalize(V v) { 93 93 return v / length(v); … … 95 95 96 96 // Project vector u onto vector v 97 forall( otype T, otypeV | dottable(V, T) | { V normalize(V); V ?*?(V, T); })97 forall(T, V | dottable(V, T) | { V normalize(V); V ?*?(V, T); }) 98 98 V project(V u, V v) { 99 99 V v_norm = normalize(v); … … 102 102 103 103 // Reflect incident vector v with respect to surface with normal n 104 forall( otype T | fromint(T), otypeV | { V project(V, V); V ?*?(T, V); V ?-?(V,V); })104 forall(T | fromint(T), V | { V project(V, V); V ?*?(T, V); V ?-?(V,V); }) 105 105 V reflect(V v, V n) { 106 106 return v - (T){2} * project(v, n); … … 111 111 // entering material (i.e., from air to water, eta = 1/1.33) 112 112 // v and n must already be normalized 113 forall( otypeT | fromint(T) | subtract(T) | multiply(T) | add(T) | lessthan(T) | sqrt(T),114 otypeV | dottable(V, T) | { V ?*?(T, V); V ?-?(V,V); void ?{}(V&, zero_t); })113 forall(T | fromint(T) | subtract(T) | multiply(T) | add(T) | lessthan(T) | sqrt(T), 114 V | dottable(V, T) | { V ?*?(T, V); V ?-?(V,V); void ?{}(V&, zero_t); }) 115 115 V refract(V v, V n, T eta) { 116 116 T dotValue = dot(n, v); … … 128 128 // i is the incident vector 129 129 // ng is the geometric normal of the surface 130 forall( otype T | lessthan(T) | zeroinit(T), otypeV | dottable(V, T) | negate(V))130 forall(T | lessthan(T) | zeroinit(T), V | dottable(V, T) | negate(V)) 131 131 V faceforward(V n, V i, V ng) { 132 132 return dot(ng, i) < (T){0} ? n : -n; -
libcfa/src/vec/vec2.hfa
rb6a8b31 rd95969a 19 19 #include "vec.hfa" 20 20 21 forall ( otypeT) {21 forall (T) { 22 22 struct vec2 { 23 23 T x, y; … … 25 25 } 26 26 27 forall ( otypeT) {27 forall (T) { 28 28 static inline { 29 29 … … 279 279 } 280 280 281 forall( dtype ostype, otypeT | writeable(T, ostype)) {281 forall(ostype &, T | writeable(T, ostype)) { 282 282 ostype & ?|?(ostype & os, vec2(T) v) with (v) { 283 283 return os | '<' | x | ',' | y | '>'; -
libcfa/src/vec/vec3.hfa
rb6a8b31 rd95969a 19 19 #include "vec.hfa" 20 20 21 forall ( otypeT) {21 forall (T) { 22 22 struct vec3 { 23 23 T x, y, z; … … 25 25 } 26 26 27 forall ( otypeT) {27 forall (T) { 28 28 static inline { 29 29 … … 288 288 } 289 289 290 forall( dtype ostype, otypeT | writeable(T, ostype)) {290 forall(ostype &, T | writeable(T, ostype)) { 291 291 ostype & ?|?(ostype & os, vec3(T) v) with (v) { 292 292 return os | '<' | x | ',' | y | ',' | z | '>'; -
libcfa/src/vec/vec4.hfa
rb6a8b31 rd95969a 19 19 #include "vec.hfa" 20 20 21 forall ( otypeT) {21 forall (T) { 22 22 struct vec4 { 23 23 T x, y, z, w; … … 25 25 } 26 26 27 forall ( otypeT) {27 forall (T) { 28 28 static inline { 29 29 … … 283 283 } 284 284 285 forall( dtype ostype, otypeT | writeable(T, ostype)) {285 forall(ostype &, T | writeable(T, ostype)) { 286 286 ostype & ?|?(ostype & os, vec4(T) v) with (v) { 287 287 return os | '<' | x | ',' | y | ',' | z | ',' | w | '>'; -
src/Parser/parser.yy
rb6a8b31 rd95969a 2441 2441 type_parameter: // CFA 2442 2442 type_class identifier_or_type_name 2443 { typedefTable.addToScope( *$2, TYPEDEFname, "9" ); } 2443 { typedefTable.addToScope( *$2, TYPEDEFname, "9" ); 2444 if ( $1 == TypeDecl::Otype ) { SemanticError( yylloc, "otype keyword is deprecated" ); } 2445 if ( $1 == TypeDecl::Dtype ) { SemanticError( yylloc, "dtype keyword is deprecated" ); } 2446 if ( $1 == TypeDecl::Ttype ) { SemanticError( yylloc, "ttype keyword is deprecated" ); } 2447 } 2444 2448 type_initializer_opt assertion_list_opt 2445 2449 { $$ = DeclarationNode::newTypeParam( $1, $2 )->addTypeInitializer( $4 )->addAssertions( $5 ); } -
src/ResolvExpr/PolyCost.cc
rb6a8b31 rd95969a 35 35 PassVisitor<PolyCost> coster( env, indexer ); 36 36 type->accept( coster ); 37 return coster.pass.result;37 return (coster.pass.result > 0) ? 1 : 0; 38 38 } 39 39 … … 87 87 ast::Pass<PolyCost_new> costing( symtab, env ); 88 88 type->accept( costing ); 89 return costing.core.result;89 return (costing.core.result > 0) ? 1 : 0; 90 90 } 91 91 -
src/ResolvExpr/SpecCost.cc
rb6a8b31 rd95969a 43 43 // mark specialization of base type 44 44 void postvisit(ReferenceType*) { if ( count >= 0 ) ++count; } 45 46 void postvisit(StructInstType*) { if ( count >= 0 ) ++count; } 47 void postvisit(UnionInstType*) { if ( count >= 0 ) ++count; } 45 48 46 49 private: … … 82 85 void previsit(StructInstType* sty) { 83 86 count = minover( sty->parameters ); 84 visit_children = false;85 87 } 86 88 … … 88 90 void previsit(UnionInstType* uty) { 89 91 count = minover( uty->parameters ); 90 visit_children = false;91 92 } 92 93 … … 174 175 void postvisit( const ast::ArrayType * ) { if ( count >= 0 ) ++count; } 175 176 void postvisit( const ast::ReferenceType * ) { if ( count >= 0 ) ++count; } 177 178 void postvisit( const ast::StructInstType * ) { if ( count >= 0 ) ++count; } 179 void postvisit( const ast::UnionInstType * ) { if ( count >= 0 ) ++count; } 176 180 177 181 // Use the minimal specialization value over returns and params. … … 189 193 void previsit( const ast::StructInstType * sty ) { 190 194 count = minimumPresent( sty->params, expr_result ); 191 visit_children = false;192 195 } 193 196 … … 195 198 void previsit( const ast::UnionInstType * uty ) { 196 199 count = minimumPresent( uty->params, expr_result ); 197 visit_children = false;198 200 } 199 201 -
tests/alloc2.cfa
rb6a8b31 rd95969a 16 16 bool passed = (malloc_size(ip) == size) && (malloc_usable_size(ip) >= size) && (malloc_alignment(ip) == align) && ((uintptr_t)ip % align == 0); 17 17 if (!passed) { 18 printf("failed test %3d: %4 lu %4lu but got %4lu ( %3lu ) %4lu\n", tests_total, size, align, malloc_size(ip), malloc_usable_size(ip), malloc_alignment(ip));18 printf("failed test %3d: %4zu %4zu but got %4zu ( %3zu ) %4zu\n", tests_total, size, align, malloc_size(ip), malloc_usable_size(ip), malloc_alignment(ip)); 19 19 tests_failed += 1; 20 20 } -
tests/avltree/avl-private.cfa
rb6a8b31 rd95969a 11 11 // an AVL tree's height is easy to compute 12 12 // just follow path with the larger balance 13 forall( otype K | Comparable(K), otypeV)13 forall(K | Comparable(K), V) 14 14 int height(tree(K, V) * t){ 15 15 int helper(tree(K, V) * t, int ht){ … … 27 27 } 28 28 29 forall( otype K | Comparable(K), otypeV)29 forall(K | Comparable(K), V) 30 30 int calcBalance(tree(K, V) * t){ 31 31 int l = height(t->left); … … 36 36 37 37 // re-establish the link between parent and child 38 forall( otype K | Comparable(K), otypeV)38 forall(K | Comparable(K), V) 39 39 void relinkToParent(tree(K, V) * t){ 40 40 tree(K, V) * parent = t->parent; // FIX ME!! … … 49 49 50 50 // rotate left from t 51 forall( otype K | Comparable(K), otypeV)51 forall(K | Comparable(K), V) 52 52 tree(K, V) * rotateLeft(tree(K, V) * t){ 53 53 tree(K, V) * newRoot = t->right; … … 68 68 69 69 // rotate right from t 70 forall( otype K | Comparable(K), otypeV)70 forall(K | Comparable(K), V) 71 71 tree(K, V) * rotateRight(tree(K, V) * t){ 72 72 tree(K, V) * newRoot = t->left; … … 87 87 88 88 // balances a node that has balance factor -2 or 2 89 forall( otype K | Comparable(K), otypeV)89 forall(K | Comparable(K), V) 90 90 tree(K, V) * fix(tree(K, V) * t){ 91 91 // ensure that t's balance factor is one of … … 113 113 114 114 // attempt to fix the tree, if necessary 115 forall( otype K | Comparable(K), otypeV)115 forall(K | Comparable(K), V) 116 116 tree(K, V) * tryFix(tree(K, V) * t){ 117 117 int b = calcBalance(t); … … 126 126 127 127 // sets parent field of c to be p 128 forall( otype K | Comparable(K), otypeV)128 forall(K | Comparable(K), V) 129 129 void setParent(tree(K, V) * c, tree(K, V) * p){ 130 130 if (! empty(c)){ -
tests/avltree/avl-private.h
rb6a8b31 rd95969a 5 5 6 6 // attempt to fix the tree, if necessary 7 forall( otype K | Comparable(K), otypeV)7 forall(K | Comparable(K), V) 8 8 tree(K, V) * tryFix(tree(K, V) * t); 9 9 10 10 // sets parent field of c to be p 11 forall( otype K | Comparable(K), otypeV)11 forall(K | Comparable(K), V) 12 12 void setParent(tree(K, V) * c, tree(K, V) * p); 13 13 14 forall( otype K | Comparable(K), otypeV)14 forall(K | Comparable(K), V) 15 15 int height(tree(K, V) * t); -
tests/avltree/avl.h
rb6a8b31 rd95969a 9 9 // #include <lib.h> 10 10 11 trait Comparable( otypeT) {11 trait Comparable(T) { 12 12 int ?<?(T, T); 13 13 }; 14 14 15 forall( otypeT | Comparable(T))15 forall(T | Comparable(T)) 16 16 int ?==?(T t1, T t2); 17 17 18 forall( otypeT | Comparable(T))18 forall(T | Comparable(T)) 19 19 int ?>?(T t1, T t2); 20 20 … … 41 41 42 42 // temporary: need forward decl to get around typedef problem 43 forall( otype K | Comparable(K), otypeV)43 forall(K | Comparable(K), V) 44 44 struct tree; 45 45 46 forall( otype K | Comparable(K), otypeV)46 forall(K | Comparable(K), V) 47 47 struct tree { 48 48 K key; … … 54 54 }; 55 55 56 forall( otype K | Comparable(K), otypeV)56 forall(K | Comparable(K), V) 57 57 void ?{}(tree(K, V) &t, K key, V value); 58 58 59 forall( otype K, otypeV)59 forall(K | Comparable(K), V) 60 60 void ^?{}(tree(K, V) & t); 61 61 62 forall( otype K | Comparable(K), otypeV)62 forall(K | Comparable(K), V) 63 63 tree(K, V) * create(K key, V value); 64 64 65 forall( otype K | Comparable(K), otypeV)65 forall(K | Comparable(K), V) 66 66 V * find(tree(K, V) * t, K key); 67 67 68 forall( otype K | Comparable(K), otypeV)68 forall(K | Comparable(K), V) 69 69 int empty(tree(K, V) * t); 70 70 71 71 // returns the root of the tree 72 forall( otype K | Comparable(K), otypeV)72 forall(K | Comparable(K), V) 73 73 int insert(tree(K, V) ** t, K key, V value); 74 74 75 forall( otype K | Comparable(K), otypeV)75 forall(K | Comparable(K), V) 76 76 int remove(tree(K, V) ** t, K key); 77 77 78 forall( otype K | Comparable(K), otypeV)78 forall(K | Comparable(K), V) 79 79 void copy(tree(K, V) * src, tree(K, V) ** ret); 80 80 81 forall( otype K | Comparable(K), otypeV)81 forall(K | Comparable(K), V) 82 82 void for_each(tree(K, V) * t, void (*func)(V)); 83 83 -
tests/avltree/avl0.cfa
rb6a8b31 rd95969a 1 1 #include "avl.h" 2 2 3 forall( otypeT | Comparable(T))3 forall(T | Comparable(T)) 4 4 int ?==?(T t1, T t2) { 5 5 return !(t1 < t2) && !(t2 < t1); 6 6 } 7 7 8 forall( otypeT | Comparable(T))8 forall(T | Comparable(T)) 9 9 int ?>?(T t1, T t2) { 10 10 return t2 < t1; -
tests/avltree/avl1.cfa
rb6a8b31 rd95969a 3 3 #include <stdlib.hfa> 4 4 5 forall( otype K | Comparable(K), otypeV)5 forall(K | Comparable(K), V) 6 6 void ?{}(tree(K, V) &t, K key, V value){ 7 7 (t.key) { key }; … … 13 13 } 14 14 15 forall( otype K, otypeV)15 forall(K| Comparable(K), V) 16 16 void ^?{}(tree(K, V) & t){ 17 17 delete(t.left); … … 21 21 } 22 22 23 forall( otype K | Comparable(K), otypeV)23 forall(K | Comparable(K), V) 24 24 tree(K, V) * create(K key, V value) { 25 25 // infinite loop trying to resolve ... t = malloc(); -
tests/avltree/avl2.cfa
rb6a8b31 rd95969a 2 2 #include "avl-private.h" 3 3 4 forall( otype K | Comparable(K), otypeV)4 forall(K | Comparable(K), V) 5 5 V * find(tree(K, V) * t, K key){ 6 6 if (empty(t)){ … … 18 18 } 19 19 20 forall( otype K | Comparable(K), otypeV)20 forall(K | Comparable(K), V) 21 21 int empty(tree(K, V) * t){ 22 22 return t == NULL; … … 24 24 25 25 // returns the root of the tree 26 forall( otype K | Comparable(K), otypeV)26 forall(K | Comparable(K), V) 27 27 int insert(tree(K, V) ** t, K key, V value) { 28 28 // handles a non-empty tree -
tests/avltree/avl3.cfa
rb6a8b31 rd95969a 4 4 5 5 // swaps the data within two tree nodes 6 forall( otype K | Comparable(K), otypeV)6 forall(K | Comparable(K), V) 7 7 void node_swap(tree(K, V) * t, tree(K, V) * t2){ 8 8 swap( t->key, t2->key); … … 11 11 12 12 // go left as deep as possible from within the right subtree 13 forall( otype K | Comparable(K), otypeV)13 forall(K | Comparable(K), V) 14 14 tree(K, V) * find_successor(tree(K, V) * t){ 15 15 tree(K, V) * find_successor_helper(tree(K, V) * t){ … … 25 25 26 26 // cleanup - don't want to deep delete, so set children to NULL first. 27 forall( otype K | Comparable(K), otypeV)27 forall(K | Comparable(K), V) 28 28 void deleteSingleNode(tree(K, V) * t) { 29 29 t->left = NULL; … … 33 33 34 34 // does the actual remove operation once we've found the node in question 35 forall( otype K | Comparable(K), otypeV)35 forall(K | Comparable(K), V) 36 36 tree(K, V) * remove_node(tree(K, V) * t){ 37 37 // is the node a leaf? … … 85 85 86 86 // finds the node that needs to be removed 87 forall( otype K | Comparable(K), otypeV)87 forall(K | Comparable(K), V) 88 88 tree(K, V) * remove_helper(tree(K, V) * t, K key, int * worked){ 89 89 if (empty(t)){ … … 106 106 } 107 107 108 forall( otype K | Comparable(K), otypeV)108 forall(K | Comparable(K), V) 109 109 int remove(tree(K, V) ** t, K key){ 110 110 int worked = 0; -
tests/avltree/avl4.cfa
rb6a8b31 rd95969a 4 4 // Perform a shallow copy of src, return the 5 5 // new tree in ret 6 forall( otype K | Comparable(K), otypeV)6 forall(K | Comparable(K), V) 7 7 int copy(tree(K, V) * src, tree(K, V) ** ret){ 8 8 tree(K, V) * helper(tree(K, V) * t, int * worked){ … … 35 35 36 36 // Apply func to every value element in t, using an in order traversal 37 forall( otype K | Comparable(K), otypeV)37 forall(K | Comparable(K), V) 38 38 void for_each(tree(K, V) * t, int (*func)(V)) { 39 39 if (t == NULL) { -
tests/bugs/10.cfa
rb6a8b31 rd95969a 2 2 // https://cforall.uwaterloo.ca/trac/ticket/10 3 3 4 forall( otypeT)4 forall(T) 5 5 struct result { 6 6 union { -
tests/bugs/104.cfa
rb6a8b31 rd95969a 4 4 [ float, float ] modf_( float x ); 5 5 6 forall( otypeT | { [T, T] modf_(T); })6 forall(T | { [T, T] modf_(T); }) 7 7 void modf(T); 8 8 -
tests/bugs/194.cfa
rb6a8b31 rd95969a 2 2 // https://cforall.uwaterloo.ca/trac/ticket/194 3 3 4 forall( dtype T| sized(T) ) T * foo( void ) {4 forall( T & | sized(T) ) T * foo( void ) { 5 5 printf( "foo1\n" ); 6 6 return (T *)0; 7 7 } 8 forall( dtype T| sized(T) ) T & foo( void ) {8 forall( T & | sized(T) ) T & foo( void ) { 9 9 printf( "foo2\n" ); 10 10 return (T &)*(T *)0; -
tests/bugs/196.cfa
rb6a8b31 rd95969a 2 2 // https://cforall.uwaterloo.ca/trac/ticket/196 3 3 4 forall( dtype T)4 forall(T &) 5 5 struct link; 6 6 7 forall( dtype T)7 forall(T &) 8 8 struct link { 9 9 link(T) * next; … … 12 12 // ----- 13 13 14 forall( dtype T)14 forall(T &) 15 15 struct foo; 16 16 17 forall( dtype U)17 forall(U &) 18 18 struct bar { 19 19 foo(U) * data; 20 20 }; 21 21 22 forall( dtype T)22 forall(T &) 23 23 struct foo {}; 24 24 -
tests/bugs/203-2.cfa
rb6a8b31 rd95969a 1 1 // Trac ticket: https://cforall.uwaterloo.ca/trac/ticket/203 2 2 3 forall( dtype A)3 forall(A &) 4 4 struct empty { 5 5 // Nothing. 6 6 }; 7 7 8 forall( dtype C)8 forall(C &) 9 9 struct wrap_e { 10 10 empty(C) field; -
tests/bugs/203-7.cfa
rb6a8b31 rd95969a 1 1 // Trac ticket: https://cforall.uwaterloo.ca/trac/ticket/203 2 2 3 forall( dtype A)3 forall(A &) 4 4 struct empty { 5 5 // Nothing. 6 6 }; 7 7 8 forall( dtype C)8 forall(C &) 9 9 struct wrap_e { 10 10 empty(C) field; -
tests/bugs/203-9.cfa
rb6a8b31 rd95969a 1 1 // Trac ticket: https://cforall.uwaterloo.ca/trac/ticket/203 2 2 3 forall( dtype A)3 forall(A &) 4 4 struct empty { 5 5 // Nothing. 6 6 }; 7 7 8 forall( dtype C)8 forall(C &) 9 9 struct wrap_e { 10 10 empty(C) field; -
tests/bugs/7.cfa
rb6a8b31 rd95969a 8 8 9 9 // (Bug 1 unresolved as of this test.) 10 forall( otypeT)10 forall(T) 11 11 struct stack_node; 12 12 13 forall( otypeT)13 forall(T) 14 14 struct stack_node { 15 15 stack_node(T) * next; … … 17 17 }; 18 18 19 forall( otypeT)19 forall(T) 20 20 struct stack { 21 21 stack_node(T) * head; 22 22 }; 23 23 24 trait stack_errors( otypeT) {24 trait stack_errors(T) { 25 25 T emptyStackHandler (stack(T) * this); 26 26 }; 27 27 28 forall( otypeT | stack_errors(T))28 forall(T | stack_errors(T)) 29 29 T pop (stack(T) * this) { 30 30 return (T){}; -
tests/castError.cfa
rb6a8b31 rd95969a 14 14 // 15 15 16 forall( otypeT) struct S { T p; };16 forall(T) struct S { T p; }; 17 17 int f; 18 18 S(int) sint; -
tests/concurrent/examples/boundedBufferEXT.cfa
rb6a8b31 rd95969a 24 24 enum { BufferSize = 50 }; 25 25 26 forall( otypeT ) {26 forall( T ) { 27 27 monitor Buffer { 28 28 int front, back, count; -
tests/concurrent/examples/boundedBufferINT.cfa
rb6a8b31 rd95969a 24 24 enum { BufferSize = 50 }; 25 25 26 forall( otypeT ) {26 forall( T ) { 27 27 monitor Buffer { 28 28 condition full, empty; -
tests/concurrent/examples/quickSort.generic.cfa
rb6a8b31 rd95969a 21 21 #include <string.h> // strcmp 22 22 23 forall( otypeT | { int ?<?( T, T ); } ) {23 forall( T | { int ?<?( T, T ); } ) { 24 24 thread Quicksort { 25 25 T * values; // communication variables -
tests/concurrent/multi-monitor.cfa
rb6a8b31 rd95969a 38 38 } 39 39 40 forall( dtype T| sized(T) | { void ^?{}(T & mutex); })40 forall(T & | sized(T) | { void ^?{}(T & mutex); }) 41 41 void delete_mutex(T * x) { 42 42 ^(*x){}; -
tests/concurrent/thread.cfa
rb6a8b31 rd95969a 1 1 #include <fstream.hfa> 2 2 #include <kernel.hfa> 3 #include <locks.hfa> 3 4 #include <stdlib.hfa> 4 5 #include <thread.hfa> -
tests/errors/completeType.cfa
rb6a8b31 rd95969a 1 1 void foo(int *) {} 2 2 void bar(void *) {} 3 forall( otypeT) void baz(T *);4 forall( dtype T) void qux(T *);5 forall( dtype T| sized(T)) void quux(T *);3 forall(T) void baz(T *); 4 forall(T &) void qux(T *); 5 forall(T & | sized(T)) void quux(T *); 6 6 7 7 struct A; // incomplete … … 39 39 40 40 41 forall( otypeT)41 forall(T) 42 42 void baz(T * x) { 43 43 // okay … … 49 49 } 50 50 51 forall( dtype T)51 forall(T &) 52 52 void qux(T * y) { 53 53 // okay … … 61 61 } 62 62 63 forall( dtype T| sized(T))63 forall(T & | sized(T)) 64 64 void quux(T * z) { 65 65 // okay -
tests/exceptions/defaults.cfa
rb6a8b31 rd95969a 55 55 56 56 void unhandled_test(void) { 57 forall( dtype T, dtype V| is_exception(T, V))57 forall(T &, V & | is_exception(T, V)) 58 58 void defaultTerminationHandler(T &) { 59 59 throw (unhandled_exception){}; -
tests/exceptions/polymorphic.cfa
rb6a8b31 rd95969a 3 3 #include <exception.hfa> 4 4 5 FORALL_TRIVIAL_EXCEPTION(proxy, ( otypeT), (T));6 FORALL_TRIVIAL_INSTANCE(proxy, ( otypeU), (U))5 FORALL_TRIVIAL_EXCEPTION(proxy, (T), (T)); 6 FORALL_TRIVIAL_INSTANCE(proxy, (U), (U)) 7 7 8 8 const char * msg(proxy(int) * this) { return "proxy(int)"; } … … 33 33 } 34 34 35 FORALL_DATA_EXCEPTION(cell, ( otypeT), (T))(35 FORALL_DATA_EXCEPTION(cell, (T), (T))( 36 36 T data; 37 37 ); 38 38 39 FORALL_DATA_INSTANCE(cell, ( otypeT), (T))39 FORALL_DATA_INSTANCE(cell, (T), (T)) 40 40 41 41 const char * msg(cell(int) * this) { return "cell(int)"; } -
tests/exceptions/virtual-poly.cfa
rb6a8b31 rd95969a 16 16 }; 17 17 18 forall( otypeT)18 forall(T) 19 19 struct mono_child_vtable { 20 20 mono_base_vtable const * const parent; 21 21 }; 22 22 23 forall( otypeT)23 forall(T) 24 24 struct mono_child { 25 25 mono_child_vtable(T) const * virtual_table; … … 37 37 } 38 38 39 forall( otypeU)39 forall(U) 40 40 struct poly_base_vtable { 41 41 poly_base_vtable(U) const * const parent; 42 42 }; 43 43 44 forall( otypeU)44 forall(U) 45 45 struct poly_base { 46 46 poly_base_vtable(U) const * virtual_table; 47 47 }; 48 48 49 forall( otypeV)49 forall(V) 50 50 struct poly_child_vtable { 51 51 poly_base_vtable(V) const * const parent; 52 52 }; 53 53 54 forall( otypeV)54 forall(V) 55 55 struct poly_child { 56 56 poly_child_vtable(V) const * virtual_table; -
tests/forall.cfa
rb6a8b31 rd95969a 15 15 16 16 void g1() { 17 forall( otypeT ) T f( T ) {};17 forall( T ) T f( T ) {}; 18 18 void f( int ) {}; 19 19 void h( void (*p)(void) ) {}; … … 32 32 33 33 void g2() { 34 forall( otypeT ) void f( T, T ) {}35 forall( otype T, otypeU ) void f( T, U ) {}34 forall( T ) void f( T, T ) {} 35 forall( T, U ) void f( T, U ) {} 36 36 37 37 int x; … … 45 45 } 46 46 47 typedef forall ( otypeT ) int (* f)( int );48 49 forall( otypeT )47 typedef forall ( T ) int (* f)( int ); 48 49 forall( T ) 50 50 void swap( T left, T right ) { 51 51 T temp = left; … … 54 54 } 55 55 56 trait sumable( otypeT ) {56 trait sumable( T ) { 57 57 void ?{}( T &, zero_t ); // 0 literal constructor 58 58 T ?+?( T, T ); // assortment of additions … … 62 62 }; // sumable 63 63 64 forall( otypeT | sumable( T ) ) // use trait64 forall( T | sumable( T ) ) // use trait 65 65 T sum( size_t size, T a[] ) { 66 66 T total = 0; // initialize by 0 constructor … … 70 70 } // sum 71 71 72 forall( otypeT | { T ?+?( T, T ); T ?++( T & ); [T] ?+=?( T &,T ); } )72 forall( T | { T ?+?( T, T ); T ?++( T & ); [T] ?+=?( T &,T ); } ) 73 73 T twice( T t ) { 74 74 return t + t; 75 75 } 76 76 77 forall( otypeT | { int ?<?(T, T); } )77 forall( T | { int ?<?(T, T); } ) 78 78 T min( T t1, T t2 ) { 79 79 return t1 < t2 ? t1 : t2; … … 91 91 92 92 // Multiple forall 93 forall( otype T ) forall( otypeS ) struct { int i; };94 forall( otype T ) struct { int i; } forall( otypeS );95 struct { int i; } forall( otype T ) forall( otypeS );96 forall( otype W ) struct { int i; } forall( otype T ) forall( otypeS );93 forall( T ) forall( S ) struct { int i; }; 94 forall( T ) struct { int i; } forall( S ); 95 struct { int i; } forall( T ) forall( S ); 96 forall( W ) struct { int i; } forall( T ) forall( S ); 97 97 98 98 // Distribution 99 99 struct P { int i; }; 100 forall( otypeT ) struct Q { T i; };101 forall( otypeT ) struct { int i; };100 forall( T ) struct Q { T i; }; 101 forall( T ) struct { int i; }; 102 102 struct KK { int i; }; 103 103 inline static { 104 104 void RT1() {} 105 105 } 106 forall( otypeT ) {106 forall( T ) { 107 107 T RT2( T ) { 108 108 typedef int TD1; 109 109 struct S1 { T t; }; 110 110 } 111 forall( otypeX ) {111 forall( X ) { 112 112 typedef int TD2; 113 113 struct S2 {}; … … 117 117 } 118 118 extern "C" { 119 forall( otypeW ) {119 forall( W ) { 120 120 W RT3( W ) {} 121 121 struct S3 {}; … … 123 123 } 124 124 void RT4() { 125 forall( otypeW ) struct S4 {};125 forall( W ) struct S4 {}; 126 126 typedef int TD3; 127 127 } … … 147 147 148 148 static inline { 149 forall( otypeT ) {149 forall( T ) { 150 150 int RT6( T p ); 151 151 } 152 forall( otype T, otypeU ) {152 forall( T, U ) { 153 153 int RT7( T, U ); 154 154 } 155 155 } 156 static forall( otypeT ) {156 static forall( T ) { 157 157 int RT8( T ); 158 158 } 159 forall( otypeT ) inline static {159 forall( T ) inline static { 160 160 int RT9( T ) { T t; return 3; } 161 161 } 162 162 163 forall( otypeT | { T ?+?( T, T ); } ) {164 forall( otypeS | { T ?+?( T, S ); } ) {165 forall( otypeW ) T bar( T t, S s ) { return t + s; }166 forall( otypeW | { W ?+?( T, W ); } ) W baz( T t, S s, W w ) { return t + s + w; }163 forall( T | { T ?+?( T, T ); } ) { 164 forall( S | { T ?+?( T, S ); } ) { 165 forall( W ) T bar( T t, S s ) { return t + s; } 166 forall( W | { W ?+?( T, W ); } ) W baz( T t, S s, W w ) { return t + s + w; } 167 167 struct W { T t; } (int,int) ww; 168 168 struct P pp; … … 170 170 } 171 171 172 forall( otype T | { T ?+?( T, T ); } ) forall( otypeS | { T ?+?( T, S ); } )172 forall( T | { T ?+?( T, T ); } ) forall( S | { T ?+?( T, S ); } ) 173 173 struct XW { T t; }; 174 174 XW(int,int) xww; 175 175 176 forall( otypeT ) struct S { T t; } (int) x, y, z;177 forall( otypeT ) struct { T t; } (int) a, b, c;178 179 forall( otype T ) static forall( otypeS ) {180 forall( otypeX ) struct U {176 forall( T ) struct S { T t; } (int) x, y, z; 177 forall( T ) struct { T t; } (int) a, b, c; 178 179 forall( T ) static forall( S ) { 180 forall( X ) struct U { 181 181 T x; 182 182 }; 183 183 } 184 184 185 forall( otypeT ) {185 forall( T ) { 186 186 extern "C" { 187 187 struct SS { T t; }; -
tests/function-operator.cfa
rb6a8b31 rd95969a 22 22 23 23 // STL-like Algorithms 24 trait Assignable( dtype T, dtype U) { T ?=?(T &, U); };25 trait Copyable( dtype T) { void ?{}(T &, T); };26 trait Destructable( dtype T) { void ^?{}(T &); };24 trait Assignable(T &, U &) { T ?=?(T &, U); }; 25 trait Copyable(T &) { void ?{}(T &, T); }; 26 trait Destructable(T &) { void ^?{}(T &); }; 27 27 28 trait Iterator( dtype iter | sized(iter) | Copyable(iter) | Destructable(iter), otypeT) {28 trait Iterator(iter & | sized(iter) | Copyable(iter) | Destructable(iter), T) { 29 29 T & *?(iter); 30 30 iter ++?(iter &); … … 32 32 }; 33 33 34 forall( otype Tin, dtype Input | Iterator(Input, Tin), otype Tout, dtype Output| Iterator(Output, Tout) | Assignable(Tout, Tin))34 forall(Tin, Input & | Iterator(Input, Tin), Tout, Output & | Iterator(Output, Tout) | Assignable(Tout, Tin)) 35 35 Output copy(Input first, Input last, Output result) { 36 36 while (first != last) { … … 42 42 43 43 // test ?()(T *, ...) -- ?() with function call-by-pointer 44 forall( otype Tin, dtype Input | Iterator(Input, Tin), otype Tout, dtype Output | Iterator(Output, Tout), otype FuncRet, dtype Func| { FuncRet ?()(Func *, Tin); } | Assignable(Tout, FuncRet))44 forall(Tin, Input & | Iterator(Input, Tin), Tout, Output & | Iterator(Output, Tout), FuncRet, Func & | { FuncRet ?()(Func *, Tin); } | Assignable(Tout, FuncRet)) 45 45 Output transform (Input first, Input last, Output result, Func * op) { 46 46 while (first != last) { … … 52 52 53 53 // test ?()(T, ...) -- ?() with function call-by-value 54 forall( dtype Iter, otype T | Iterator(Iter, T), otypePred | { int ?()(Pred, T); })54 forall(Iter &, T | Iterator(Iter, T), Pred | { int ?()(Pred, T); }) 55 55 Iter find_if (Iter first, Iter last, Pred pred) { 56 56 while (first != last) { … … 62 62 63 63 // test ?()(T, ...) -- ?() with function call-by-reference 64 forall( otype Generator, otype GenRet | { GenRet ?()(Generator &); }, dtype Iter, otypeT | Iterator(Iter, T) | Assignable(T, GenRet))64 forall(Generator, GenRet | { GenRet ?()(Generator &); }, Iter &, T | Iterator(Iter, T) | Assignable(T, GenRet)) 65 65 void generate(Iter first, Iter last, Generator & gen) { 66 66 int i = 0; … … 108 108 } 109 109 110 forall( otypeT | { int ?==?(T, T); })110 forall(T | { int ?==?(T, T); }) 111 111 struct Equals { 112 112 T val; 113 113 }; 114 114 115 forall( otypeT | { int ?==?(T, T); })115 forall(T | { int ?==?(T, T); }) 116 116 int ?()(Equals(T) eq, T x) { 117 117 return eq.val == x; 118 118 } 119 119 120 forall( otypeT | { T ?*?(T, T); })120 forall(T | { T ?*?(T, T); }) 121 121 struct Multiply { 122 122 T val; 123 123 }; 124 124 125 forall( otypeT | { T ?*?(T, T); })125 forall(T | { T ?*?(T, T); }) 126 126 T ?()(Multiply(T) * mult, T x) { 127 127 return mult->val * x; … … 130 130 // TODO: generalize to ttype return; doesn't work yet 131 131 // like std::function 132 forall( otype Return, ttype Args)132 forall(Return, Args...) 133 133 struct function { 134 134 Return (*f)(Args); -
tests/genericUnion.cfa
rb6a8b31 rd95969a 16 16 #include <limits.hfa> 17 17 18 forall( otypeT)18 forall(T) 19 19 union ByteView { 20 20 T val; … … 22 22 }; 23 23 24 forall( otypeT)24 forall(T) 25 25 void print(ByteView(T) x) { 26 26 for (int i = 0; i < sizeof(int); i++) { // want to change to sizeof(T) … … 29 29 } 30 30 31 forall( otypeT)31 forall(T) 32 32 void f(ByteView(T) x, T val) { 33 33 print(x); -
tests/global-monomorph.cfa
rb6a8b31 rd95969a 1 1 // Create monomorphic instances of polymorphic types at global scope. 2 2 3 forall( dtype T)3 forall(T &) 4 4 void poly0(T &) {} 5 5 6 forall( dtype T| sized(T))6 forall(T & | sized(T)) 7 7 void poly1(T &) {} 8 8 9 forall( otypeT)9 forall(T) 10 10 void poly2(T &) {} 11 11 -
tests/identity.cfa
rb6a8b31 rd95969a 16 16 #include <fstream.hfa> 17 17 18 forall( otypeT )18 forall( T ) 19 19 T identity( T t ) { 20 20 return t; -
tests/init1.cfa
rb6a8b31 rd95969a 120 120 } 121 121 122 forall ( dtype T, dtype S)122 forall (T &, S &) 123 123 T & anycvt( S & s ) { 124 124 return s; // mismatched referenced type 125 125 } 126 126 127 forall ( dtype T, dtype S)127 forall (T &, S &) 128 128 T * anycvt( S * s ) { 129 129 return s; // mismatched referenced type -
tests/nested-types.cfa
rb6a8b31 rd95969a 16 16 typedef int N; 17 17 struct A { 18 forall( otypeT)18 forall(T) 19 19 struct N { 20 20 T x; -
tests/poly-d-cycle.cfa
rb6a8b31 rd95969a 1 1 // Check that a cycle of polymorphic dtype structures can be instancated. 2 2 3 forall( dtype T)3 forall(T &) 4 4 struct func_table; 5 5 6 forall( dtype U)6 forall(U &) 7 7 struct object { 8 8 func_table(U) * virtual_table; 9 9 }; 10 10 11 forall( dtype T)11 forall(T &) 12 12 struct func_table { 13 13 void (*object_func)(object(T) *); -
tests/poly-o-cycle.cfa
rb6a8b31 rd95969a 1 1 // Check that a cycle of polymorphic otype structures can be instancated. 2 2 3 forall( otypeT)3 forall(T) 4 4 struct func_table; 5 5 6 forall( otypeU)6 forall(U) 7 7 struct object { 8 8 func_table(U) * virtual_table; 9 9 }; 10 10 11 forall( otypeT)11 forall(T) 12 12 struct func_table { 13 13 void (*object_func)(object(T) *); -
tests/polymorphism.cfa
rb6a8b31 rd95969a 18 18 #include <fstream.hfa> 19 19 20 forall( otypeT)20 forall(T) 21 21 T f(T x, T y) { 22 22 x = y; … … 24 24 } 25 25 26 forall( otypeT) T ident(T x) {26 forall(T) T ident(T x) { 27 27 return x; 28 28 } 29 29 30 forall( otype T, otypeU )30 forall( T, U ) 31 31 size_t struct_size( T i, U j ) { 32 32 struct S { T i; U j; }; … … 34 34 } 35 35 36 forall( otype T, otypeU )36 forall( T, U ) 37 37 size_t union_size( T i, U j ) { 38 38 union B { T i; U j; }; … … 41 41 42 42 // perform some simple operations on aggregates of T and U 43 forall( otype T | { void print(T); int ?==?(T, T); }, otypeU | { void print(U); U ?=?(U&, zero_t); } )43 forall( T | { void print(T); int ?==?(T, T); }, U | { void print(U); U ?=?(U&, zero_t); } ) 44 44 U foo(T i, U j) { 45 45 struct S { T i; U j; }; -
tests/raii/ctor-autogen.cfa
rb6a8b31 rd95969a 33 33 34 34 // dtype-static generic type is otype 35 forall( dtype T)35 forall(T &) 36 36 struct DtypeStaticStruct { 37 37 T * data; … … 39 39 }; 40 40 41 forall( dtype T)41 forall(T &) 42 42 union DtypeStaticUnion { 43 43 T * data; … … 46 46 47 47 // dynamic generic type is otype 48 forall( otypeT)48 forall(T) 49 49 struct DynamicStruct { 50 50 T x; 51 51 }; 52 52 53 forall( otypeT)53 forall(T) 54 54 union DynamicUnion { 55 55 T x; … … 80 80 81 81 82 forall( otypeT)82 forall(T) 83 83 T identity(T x) { return x; } 84 84 -
tests/simpleGenericTriple.cfa
rb6a8b31 rd95969a 14 14 // 15 15 16 forall( otypeT)16 forall(T) 17 17 struct T3 { 18 18 T f0, f1, f2; 19 19 }; 20 20 21 forall( otypeT | { T ?+?(T, T); })21 forall(T | { T ?+?(T, T); }) 22 22 T3(T) ?+?(T3(T) x, T3(T) y) { 23 23 T3(T) z = { x.f0+y.f0, x.f1+y.f1, x.f2+y.f2 }; -
tests/sum.cfa
rb6a8b31 rd95969a 18 18 #include <stdlib.hfa> 19 19 20 trait sumable( otypeT ) {20 trait sumable( T ) { 21 21 void ?{}( T &, zero_t ); // 0 literal constructor 22 22 T ?+?( T, T ); // assortment of additions … … 26 26 }; // sumable 27 27 28 forall( otypeT | sumable( T ) ) // use trait28 forall( T | sumable( T ) ) // use trait 29 29 T sum( size_t size, T a[] ) { 30 30 T total = 0; // initialize by 0 constructor … … 107 107 | sum( size, (S *)a ) | ", check" | (S)s; 108 108 109 forall( otypeImpl | sumable( Impl ) )109 forall( Impl | sumable( Impl ) ) 110 110 struct GS { 111 111 Impl * x, * y; … … 194 194 sum( size, (S *)a ).[i, j], s.[i, j] ); 195 195 196 forall( otypeImpl | sumable( Impl ) )196 forall( Impl | sumable( Impl ) ) 197 197 struct GS { 198 198 Impl * x, * y; -
tests/tuple/tuplePolymorphism.cfa
rb6a8b31 rd95969a 29 29 // ensure that f is a viable candidate for g, even though its parameter structure does not exactly match 30 30 [A] f([A, B] x, B y) { printf("%g %c %g %lld %c %lld %lld %c %lld\n", x.0.[x,y,z], x.1.[x,y,z], y.[x,y,z]); return x.0; } 31 forall( otype T, otypeU | { T f(T, U, U); })31 forall(T, U | { T f(T, U, U); }) 32 32 void g(T x, U y) { f(x, y, y); } 33 33 34 34 // add two triples 35 forall( otypeT | { T ?+?(T, T); })35 forall(T | { T ?+?(T, T); }) 36 36 [T, T, T] ?+?([T, T, T] x, [T, T, T] y) { 37 37 return [x.0+y.0, x.1+y.1, x.2+y.2]; … … 64 64 } 65 65 66 forall( otypeT)66 forall(T) 67 67 [T, T] foo([T, T] y) { 68 68 [T, T] x; -
tests/tuple/tupleVariadic.cfa
rb6a8b31 rd95969a 19 19 printf("called func(void)\n"); 20 20 } 21 forall( otype T, ttype Params| { void process(T); void func(Params); })21 forall(T, Params... | { void process(T); void func(Params); }) 22 22 void func(T arg1, Params p) { 23 23 process(arg1); … … 92 92 } 93 93 94 forall( otypeT)94 forall(T) 95 95 T * copy(T x) { 96 96 // test calling new inside a polymorphic function … … 98 98 } 99 99 100 forall( ttype T| { void foo(T); }) void bar(T x) {}100 forall(T... | { void foo(T); }) void bar(T x) {} 101 101 void foo(int) {} 102 102 -
tests/zombies/ArrayN.c
rb6a8b31 rd95969a 6 6 // } 7 7 8 forall( otypeindex_t)8 forall(index_t) 9 9 index_t offset_to_index(unsigned offset, index_t size) { 10 10 return [offset / size.0, offset % size.1]; -
tests/zombies/Members.c
rb6a8b31 rd95969a 2 2 int ?=?( int*, int ); 3 3 float ?=?( float*, float ); 4 forall( dtype DT) DT * ?=?( DT**, DT* );5 forall( otypeT) lvalue T *?( T* );4 forall( DT & ) DT * ?=?( DT**, DT* ); 5 forall(T) lvalue T *?( T* ); 6 6 char *__builtin_memcpy(); 7 7 -
tests/zombies/Rank2.c
rb6a8b31 rd95969a 1 1 int ?=?( int &, int ); 2 forall( dtype DT) DT * ?=?( DT *&, DT * );2 forall(DT &) DT * ?=?( DT *&, DT * ); 3 3 4 4 void a() { 5 forall( otypeT ) void f( T );6 void g( forall( otypeU ) void p( U ) );5 forall( T ) void f( T ); 6 void g( forall( U ) void p( U ) ); 7 7 g( f ); 8 8 } … … 10 10 void g() { 11 11 void h( int *null ); 12 forall( otypeT ) T id( T );12 forall( T ) T id( T ); 13 13 // forall( dtype T ) T *0; 14 14 // int 0; -
tests/zombies/abstype.c
rb6a8b31 rd95969a 21 21 } 22 22 23 forall( otypeT ) T *?( T * );23 forall( T ) T *?( T * ); 24 24 int ?++( int * ); 25 25 int ?=?( int *, int ); 26 forall( dtype DT) DT * ?=?( DT **, DT * );26 forall( DT & ) DT * ?=?( DT **, DT * ); 27 27 28 28 otype U = int *; -
tests/zombies/context.cfa
rb6a8b31 rd95969a 1 1 // trait declaration 2 2 3 trait has_q( otypeT ) {3 trait has_q( T ) { 4 4 T q( T ); 5 5 }; 6 6 7 forall( otypez | has_q( z ) ) void f() {8 trait has_r( otype T, otypeU ) {7 forall( z | has_q( z ) ) void f() { 8 trait has_r( T, U ) { 9 9 T r( T, T (T,U) ); 10 10 }; -
tests/zombies/gc_no_raii/bug-repro/blockers/explicit_cast.c
rb6a8b31 rd95969a 9 9 }; 10 10 11 forall( otypeT)11 forall(T) 12 12 struct gcpointer 13 13 { … … 15 15 }; 16 16 17 forall( otypeT)17 forall(T) 18 18 static inline gcpointer(T) gcmalloc() 19 19 { -
tests/zombies/gc_no_raii/bug-repro/blockers/recursive_realloc.c
rb6a8b31 rd95969a 3 3 #include <stdlib.hfa> 4 4 5 trait allocator_c( otype T, otypeallocator_t)5 trait allocator_c(T, allocator_t) 6 6 { 7 7 void realloc(allocator_t* const, size_t); 8 8 }; 9 9 10 forall( otypeT)10 forall(T) 11 11 struct heap_allocator 12 12 { … … 15 15 }; 16 16 17 forall( otypeT)17 forall(T) 18 18 inline void realloc(heap_allocator(T) *const this, size_t size) 19 19 { -
tests/zombies/gc_no_raii/bug-repro/deref.c
rb6a8b31 rd95969a 1 forall( otypeT)1 forall(T) 2 2 struct wrap 3 3 { … … 5 5 }; 6 6 7 forall( otypeT)7 forall(T) 8 8 T *? (wrap(T) rhs) 9 9 { -
tests/zombies/gc_no_raii/bug-repro/field.c
rb6a8b31 rd95969a 8 8 //------------------------------------------------------------------------------ 9 9 //Declaration 10 trait allocator_c( otype T, otypeallocator_t)10 trait allocator_c(T, allocator_t) 11 11 { 12 12 void ctor(allocator_t* const); … … 16 16 }; 17 17 18 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))18 forall(T, allocator_t | allocator_c(T, allocator_t)) 19 19 struct vector 20 20 { -
tests/zombies/gc_no_raii/bug-repro/malloc.c
rb6a8b31 rd95969a 1 forall( otypeT)1 forall(T) 2 2 struct wrapper 3 3 { … … 5 5 }; 6 6 7 forall( otypeT)7 forall(T) 8 8 void ctor(wrapper(T)* this) 9 9 { … … 11 11 } 12 12 13 forall( otypeT)13 forall(T) 14 14 wrapper(T) gcmalloc() 15 15 { … … 19 19 } 20 20 21 forall( otypeT)21 forall(T) 22 22 wrapper(T)* ?=? (wrapper(T)* lhs, wrapper(T)* rhs) 23 23 { -
tests/zombies/gc_no_raii/bug-repro/oddtype.c
rb6a8b31 rd95969a 1 forall( dtype T)1 forall(T &) 2 2 struct wrap { 3 3 int i; 4 4 }; 5 5 6 forall( otypeT) void ?{}(wrap(T)* this) {}7 forall( otypeT) void ?=?(wrap(T)* this) {}8 forall( otypeT) void ^?{}(wrap(T)* this) {}6 forall(T) void ?{}(wrap(T)* this) {} 7 forall(T) void ?=?(wrap(T)* this) {} 8 forall(T) void ^?{}(wrap(T)* this) {} 9 9 10 10 struct List_t { -
tests/zombies/gc_no_raii/bug-repro/push_back.h
rb6a8b31 rd95969a 1 1 //------------------------------------------------------------------------------ 2 2 //Declaration 3 trait allocator_c( otype T, otypeallocator_t) {3 trait allocator_c(T, allocator_t) { 4 4 void ctor(allocator_t* const); 5 5 void dtor(allocator_t* const); … … 8 8 }; 9 9 10 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))10 forall(T, allocator_t | allocator_c(T, allocator_t)) 11 11 struct vector 12 12 { … … 17 17 //------------------------------------------------------------------------------ 18 18 //Initialization 19 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))19 forall(T, allocator_t | allocator_c(T, allocator_t)) 20 20 void vector_ctor(vector(T, allocator_t) *const this); 21 21 22 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))22 forall(T, allocator_t | allocator_c(T, allocator_t)) 23 23 void dtor(vector(T, allocator_t) *const this); 24 24 25 25 //------------------------------------------------------------------------------ 26 26 //Allocator 27 forall( otypeT)27 forall(T) 28 28 struct heap_allocator 29 29 { … … 32 32 }; 33 33 34 forall( otypeT)34 forall(T) 35 35 void ctor(heap_allocator(T) *const this); 36 36 37 forall( otypeT)37 forall(T) 38 38 void dtor(heap_allocator(T) *const this); 39 39 40 forall( otypeT)40 forall(T) 41 41 void realloc(heap_allocator(T) *const this, size_t size); 42 42 43 forall( otypeT)43 forall(T) 44 44 inline T* data(heap_allocator(T) *const this) 45 45 { … … 49 49 //------------------------------------------------------------------------------ 50 50 //Capacity 51 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))51 forall(T, allocator_t | allocator_c(T, allocator_t)) 52 52 inline bool empty(vector(T, allocator_t) *const this) 53 53 { … … 55 55 } 56 56 57 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))57 forall(T, allocator_t | allocator_c(T, allocator_t)) 58 58 inline bool size(vector(T, allocator_t) *const this) 59 59 { … … 61 61 } 62 62 63 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))63 forall(T, allocator_t | allocator_c(T, allocator_t)) 64 64 inline void reserve(vector(T, allocator_t) *const this, size_t size) 65 65 { … … 69 69 //------------------------------------------------------------------------------ 70 70 //Modifiers 71 forall( otype T, otypeallocator_t | allocator_c(T, allocator_t))71 forall(T, allocator_t | allocator_c(T, allocator_t)) 72 72 void push_back(vector(T, allocator_t) *const this, T value); -
tests/zombies/gc_no_raii/bug-repro/realloc.c
rb6a8b31 rd95969a 1 1 void* realloc(void*, unsigned long int); 2 2 3 forall( otypeT)3 forall(T) 4 4 struct wrap 5 5 { … … 7 7 }; 8 8 9 forall( otypeT)9 forall(T) 10 10 static inline void realloc(wrap(T) *const this, unsigned long int size) 11 11 { -
tests/zombies/gc_no_raii/bug-repro/return.c
rb6a8b31 rd95969a 1 forall( otypeT)1 forall(T) 2 2 struct wrapper 3 3 { … … 5 5 }; 6 6 7 forall( otypeT)7 forall(T) 8 8 wrapper(T) create() 9 9 { … … 12 12 } 13 13 14 forall( otypeT)14 forall(T) 15 15 wrapper(T)* ?=?(wrapper(T)* lhs, wrapper(T)* rhs) 16 16 { -
tests/zombies/gc_no_raii/bug-repro/return_template.c
rb6a8b31 rd95969a 1 forall( otypeT)1 forall(T) 2 2 struct wrap 3 3 { … … 5 5 }; 6 6 7 forall( otypeT) void ?{}(wrap(T)* this);8 forall( otypeT) void ?{}(wrap(T)* this, wrap(T)* rhs);9 forall( otypeT) void ^?{}(wrap(T)* this);10 forall( otypeT) void ?=?(wrap(T)* this, wrap(T)* rhs);7 forall(T) void ?{}(wrap(T)* this); 8 forall(T) void ?{}(wrap(T)* this, wrap(T)* rhs); 9 forall(T) void ^?{}(wrap(T)* this); 10 forall(T) void ?=?(wrap(T)* this, wrap(T)* rhs); 11 11 12 forall( otypeT)12 forall(T) 13 13 wrap(T) test() 14 14 { -
tests/zombies/gc_no_raii/bug-repro/slow_malloc.c
rb6a8b31 rd95969a 1 1 #include <stdlib.hfa> 2 2 3 forall( otypeT)3 forall(T) 4 4 struct heap_allocator 5 5 { -
tests/zombies/gc_no_raii/bug-repro/zero.c
rb6a8b31 rd95969a 1 forall( otypeT)1 forall(T) 2 2 struct wrap 3 3 { … … 5 5 }; 6 6 7 forall( otypeT)7 forall(T) 8 8 int ?==? (wrap(T) lhs, wrap(T) rhs) 9 9 { … … 14 14 struct wrap(int) 0; 15 15 /*/ 16 forall( otypeT)16 forall(T) 17 17 struct wrap(T) 0; 18 18 //*/ -
tests/zombies/gc_no_raii/src/gc.h
rb6a8b31 rd95969a 13 13 // } 14 14 15 forall( otypeT)15 forall(T) 16 16 static inline void gcmalloc(gcpointer(T)* ptr) 17 17 { -
tests/zombies/gc_no_raii/src/gcpointers.c
rb6a8b31 rd95969a 113 113 #endif 114 114 115 forall( otypeT) void ?{}(gcpointer(T)* this) {115 forall(T) void ?{}(gcpointer(T)* this) { 116 116 (&this->internal) {}; 117 117 } 118 118 119 forall( otypeT) void ?{}(gcpointer(T)* this, void* address) {119 forall(T) void ?{}(gcpointer(T)* this, void* address) { 120 120 (&this->internal) { address }; 121 121 } 122 122 123 forall( otypeT) void ?{}(gcpointer(T)* this, gcpointer(T) other) {123 forall(T) void ?{}(gcpointer(T)* this, gcpointer(T) other) { 124 124 (&this->internal) { other.internal }; 125 125 } 126 126 127 forall( otypeT) void ^?{}(gcpointer(T)* this) {127 forall(T) void ^?{}(gcpointer(T)* this) { 128 128 ^?{}(&this->internal); 129 129 } 130 130 131 forall( otypeT) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs) {131 forall(T) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs) { 132 132 this->internal = rhs.internal; 133 133 return *this; … … 136 136 // forall(otype T) T *?(gcpointer(T) this); 137 137 138 forall( otypeT) T* get(gcpointer(T)* this) {138 forall(T) T* get(gcpointer(T)* this) { 139 139 return (T*)this->internal.ptr; 140 140 } 141 141 // 142 142 // //Logical operators 143 forall( otypeT) int ?!=?(gcpointer(T) this, int zero) {143 forall(T) int ?!=?(gcpointer(T) this, int zero) { 144 144 return this.internal.ptr != 0; 145 145 } -
tests/zombies/gc_no_raii/src/gcpointers.h
rb6a8b31 rd95969a 4 4 #include <stdint.h> 5 5 6 forall( dtype T)6 forall(T &) 7 7 struct gcpointer; 8 8 … … 29 29 #endif 30 30 31 forall( dtype T)31 forall(T &) 32 32 struct gcpointer 33 33 { … … 36 36 37 37 // 38 forall( otypeT) void ?{}(gcpointer(T)* this);39 forall( otypeT) void ?{}(gcpointer(T)* this, void* address);40 forall( otypeT) void ?{}(gcpointer(T)* this, gcpointer(T) other);41 forall( otypeT) void ^?{}(gcpointer(T)* this);42 forall( otypeT) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs);38 forall(T) void ?{}(gcpointer(T)* this); 39 forall(T) void ?{}(gcpointer(T)* this, void* address); 40 forall(T) void ?{}(gcpointer(T)* this, gcpointer(T) other); 41 forall(T) void ^?{}(gcpointer(T)* this); 42 forall(T) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs); 43 43 44 44 45 45 // forall(otype T) T *?(gcpointer(T) this); 46 forall( otypeT) T* get(gcpointer(T)* this);46 forall(T) T* get(gcpointer(T)* this); 47 47 48 48 //Logical operators 49 forall( otypeT) int ?!=?(gcpointer(T) this, int zero);50 forall( otypeT) int ?!=?(gcpointer(T) this, gcpointer(T) rhs);51 forall( otypeT) int ?==?(gcpointer(T) this, gcpointer(T) rhs);49 forall(T) int ?!=?(gcpointer(T) this, int zero); 50 forall(T) int ?!=?(gcpointer(T) this, gcpointer(T) rhs); 51 forall(T) int ?==?(gcpointer(T) this, gcpointer(T) rhs); -
tests/zombies/gc_no_raii/src/tools.h
rb6a8b31 rd95969a 12 12 // } 13 13 14 trait has_equal( otypeT)14 trait has_equal(T) 15 15 { 16 16 signed int ?==?(T a, T b); 17 17 }; 18 18 19 trait InputIterator_t( otype T, otypeInputIterator)19 trait InputIterator_t(T, InputIterator) 20 20 { 21 21 signed int ?==?(InputIterator a, InputIterator b); … … 26 26 }; 27 27 28 forall( otype T | has_equal(T), otypeInputIterator | InputIterator_t(T, InputIterator))28 forall(T | has_equal(T), InputIterator | InputIterator_t(T, InputIterator)) 29 29 static inline InputIterator find( InputIterator first, const InputIterator* const last, T val) 30 30 { -
tests/zombies/hashtable.cfa
rb6a8b31 rd95969a 14 14 15 15 16 trait has_hash( otypeK ) {16 trait has_hash( K ) { 17 17 size_t hash(K); 18 18 int ?==?( K, K ); 19 19 }; 20 20 21 trait hkey( otype K, dtype tN| has_hash(K) ) {21 trait hkey( K, tN & | has_hash(K) ) { 22 22 K key(tN &); 23 23 }; 24 24 25 forall( otype K, dtype tN, dtype tE| $dlistable(tN, tE) | hkey(K, tN) ) {25 forall( K, tN &, tE & | $dlistable(tN, tE) | hkey(K, tN) ) { 26 26 27 27 struct hashtable { … … 39 39 } 40 40 41 forall( otype K, dtype tN, dtype tE| $dlistable(tN, tE) | hkey(K, tN) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) {41 forall( K, tN &, tE & | $dlistable(tN, tE) | hkey(K, tN) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) { 42 42 43 43 void ?{}( hashtable(K, tN, tE) & this, size_t n_buckets, dlist(tN, tE) *buckets ) { … … 57 57 } 58 58 59 forall( otype K, dtype tN, dtype tE| $dlistable(tN, tE) | hkey(K, tN) ) {59 forall( K, tN &, tE & | $dlistable(tN, tE) | hkey(K, tN) ) { 60 60 61 61 float fill_frac( hashtable(K, tN, tE) & this ) with(this) { … … 124 124 125 125 126 trait heaped( dtype T) {126 trait heaped(T &) { 127 127 T * alloc( size_t ); 128 128 void free( void * ); … … 133 133 } 134 134 135 forall( otype K, dtype tN, dtype tE| $dlistable(tN, tE) | hkey(K, tN) | heaped( dlist(tN, tE) ) ) {135 forall( K, tN &, tE & | $dlistable(tN, tE) | hkey(K, tN) | heaped( dlist(tN, tE) ) ) { 136 136 137 137 struct hashtable_dynamic { -
tests/zombies/hashtable2.cfa
rb6a8b31 rd95969a 69 69 70 70 71 trait pretendsToMatter( dtype TTT) {71 trait pretendsToMatter( TTT & ) { 72 72 void actsmart(TTT &); 73 73 }; 74 74 75 forall( dtype TTTx)75 forall( TTTx & ) 76 76 void actsmart(TTTx &) {} 77 77 … … 86 86 // 2. shows up in -CFA output as hashtable_rbs(), which is bad C; expecting hashtable_rbs* 87 87 88 forall( otypeTt_unused | pretendsToMatter(Tt_unused) ) {88 forall( Tt_unused | pretendsToMatter(Tt_unused) ) { 89 89 90 90 // hashtable of request by source … … 104 104 } 105 105 106 forall( otypeTt_unused | pretendsToMatter(Tt_unused) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) {106 forall( Tt_unused | pretendsToMatter(Tt_unused) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) { 107 107 108 108 void ?{}( hashtable_rbs(Tt_unused) & this, size_t n_buckets, dlist(request_in_ht_by_src, request) *buckets, … … 135 135 void defaultResumptionHandler( ht_auto_resize_pending & ex ); 136 136 137 forall( otypeTt_unused | pretendsToMatter(Tt_unused) ) {137 forall( Tt_unused | pretendsToMatter(Tt_unused) ) { 138 138 139 139 float fill_frac( hashtable_rbs(Tt_unused) & this ) with(this) { … … 221 221 222 222 223 trait heaped( dtype T) {223 trait heaped(T &) { 224 224 T * alloc( size_t ); 225 225 void free( void * ); … … 228 228 void __dynamic_defaultResumptionHandler(ht_fill_limit_crossed &); 229 229 230 forall( otypeTt_unused ) {230 forall( Tt_unused ) { 231 231 232 232 struct hashtable_rbs_dynamic { … … 263 263 264 264 265 forall( otypeTt_unused | heaped( dlist(request_in_ht_by_src, request) ) ) {265 forall( Tt_unused | heaped( dlist(request_in_ht_by_src, request) ) ) { 266 266 267 267 void ?{}( hashtable_rbs_dynamic(Tt_unused).resize_policy & this, size_t nbuckets_floor ) { … … 325 325 } 326 326 327 forall( otypeTt_unused ) {327 forall( Tt_unused ) { 328 328 void rehashToLarger_STEP( hashtable_rbs_dynamic(Tt_unused) & this, size_t new_n_buckets ) with (this) { 329 329 rehashToLarger( this, new_n_buckets ); -
tests/zombies/huge.c
rb6a8b31 rd95969a 14 14 // 15 15 16 int huge( int n, forall( otypeT ) T (*f)( T ) ) {16 int huge( int n, forall( T ) T (*f)( T ) ) { 17 17 if ( n <= 0 ) 18 18 return f( 0 ); -
tests/zombies/it_out.c
rb6a8b31 rd95969a 16 16 typedef unsigned long streamsize_type; 17 17 18 trait ostream( dtype os_type) {18 trait ostream( os_type & ) { 19 19 os_type *write( os_type *, const char *, streamsize_type ); 20 20 int fail( os_type * ); 21 21 }; 22 22 23 trait writeable( otypeT ) {24 forall( dtype os_type| ostream( os_type ) ) os_type * ?<<?( os_type *, T );23 trait writeable( T ) { 24 forall( os_type & | ostream( os_type ) ) os_type * ?<<?( os_type *, T ); 25 25 }; 26 26 27 forall( dtype os_type| ostream( os_type ) ) os_type * ?<<?( os_type *, char );28 forall( dtype os_type| ostream( os_type ) ) os_type * ?<<?( os_type *, int );29 forall( dtype os_type| ostream( os_type ) ) os_type * ?<<?( os_type *, const char * );27 forall( os_type & | ostream( os_type ) ) os_type * ?<<?( os_type *, char ); 28 forall( os_type & | ostream( os_type ) ) os_type * ?<<?( os_type *, int ); 29 forall( os_type & | ostream( os_type ) ) os_type * ?<<?( os_type *, const char * ); 30 30 31 trait istream( dtype is_type) {31 trait istream( is_type & ) { 32 32 is_type *read( is_type *, char *, streamsize_type ); 33 33 is_type *unread( is_type *, char ); … … 36 36 }; 37 37 38 trait readable( otypeT ) {39 forall( dtype is_type| istream( is_type ) ) is_type * ?<<?( is_type *, T );38 trait readable( T ) { 39 forall( is_type & | istream( is_type ) ) is_type * ?<<?( is_type *, T ); 40 40 }; 41 41 42 forall( dtype is_type| istream( is_type ) ) is_type * ?>>?( is_type *, char* );43 forall( dtype is_type| istream( is_type ) ) is_type * ?>>?( is_type *, int* );42 forall( is_type & | istream( is_type ) ) is_type * ?>>?( is_type *, char* ); 43 forall( is_type & | istream( is_type ) ) is_type * ?>>?( is_type *, int* ); 44 44 45 trait iterator( otype iterator_type, otypeelt_type ) {45 trait iterator( iterator_type, elt_type ) { 46 46 iterator_type ?++( iterator_type* ); 47 47 iterator_type ++?( iterator_type* ); … … 52 52 }; 53 53 54 forall( otypeelt_type | writeable( elt_type ),55 otypeiterator_type | iterator( iterator_type, elt_type ),56 dtype os_type| ostream( os_type ) )54 forall( elt_type | writeable( elt_type ), 55 iterator_type | iterator( iterator_type, elt_type ), 56 os_type & | ostream( os_type ) ) 57 57 void write_all( iterator_type begin, iterator_type end, os_type *os ); 58 58 59 forall( otypeelt_type | writeable( elt_type ),60 otypeiterator_type | iterator( iterator_type, elt_type ),61 dtype os_type| ostream( os_type ) )59 forall( elt_type | writeable( elt_type ), 60 iterator_type | iterator( iterator_type, elt_type ), 61 os_type & | ostream( os_type ) ) 62 62 void write_all( elt_type begin, iterator_type end, os_type *os ) { 63 63 os << begin; -
tests/zombies/new.c
rb6a8b31 rd95969a 14 14 // 15 15 16 forall( otypeT )16 forall( T ) 17 17 void f( T *t ) { 18 18 t--; -
tests/zombies/occursError.cfa
rb6a8b31 rd95969a 1 forall( otypeT ) void f( void (*)( T, T * ) );2 forall( otypeU ) void g( U, U * );3 forall( otypeU ) void h( U *, U );1 forall( T ) void f( void (*)( T, T * ) ); 2 forall( U ) void g( U, U * ); 3 forall( U ) void h( U *, U ); 4 4 5 5 void test() { -
tests/zombies/prolog.c
rb6a8b31 rd95969a 25 25 void is_integer( int x ) {} 26 26 27 trait ArithmeticType( otypeT ) {27 trait ArithmeticType( T ) { 28 28 void is_arithmetic( T ); 29 29 }; 30 30 31 trait IntegralType( otypeT | ArithmeticType( T ) ) {31 trait IntegralType( T | ArithmeticType( T ) ) { 32 32 void is_integer( T ); 33 33 }; 34 34 35 forall( otypeT | IntegralType( T ) | { void printResult( T ); } )35 forall( T | IntegralType( T ) | { void printResult( T ); } ) 36 36 void hornclause( T param ) { 37 37 printResult( param ); -
tests/zombies/quad.c
rb6a8b31 rd95969a 16 16 #include <fstream.hfa> 17 17 18 forall( otypeT | { T ?*?( T, T ); } )18 forall( T | { T ?*?( T, T ); } ) 19 19 T square( T t ) { 20 20 return t * t; 21 21 } 22 22 23 forall( otypeU | { U square( U ); } )23 forall( U | { U square( U ); } ) 24 24 U quad( U u ) { 25 25 return square( square( u ) ); -
tests/zombies/scope.cfa
rb6a8b31 rd95969a 20 20 y p; 21 21 22 trait has_u( otypez ) {22 trait has_u( z ) { 23 23 z u(z); 24 24 }; 25 25 26 forall( otypet | has_u( t ) )26 forall( t | has_u( t ) ) 27 27 y q( t the_t ) { 28 28 t y = u( the_t ); -
tests/zombies/simplePoly.c
rb6a8b31 rd95969a 14 14 // 15 15 16 forall( otype T, otypeU | { T f( T, U ); } )16 forall( T, U | { T f( T, U ); } ) 17 17 T q( T t, U u ) { 18 18 return f( t, u ); -
tests/zombies/simpler.c
rb6a8b31 rd95969a 14 14 // 15 15 16 forall( otypeT ) T id( T, T );16 forall( T ) T id( T, T ); 17 17 18 18 int main() { -
tests/zombies/specialize.c
rb6a8b31 rd95969a 39 39 } 40 40 41 forall( otypeT ) T f( T t )41 forall( T ) T f( T t ) 42 42 { 43 43 printf( "in f; sizeof T is %d\n", sizeof( T ) ); -
tests/zombies/square.c
rb6a8b31 rd95969a 16 16 #include <fstream.hfa> 17 17 18 forall( otypeT | { T ?*?( T, T ); } )18 forall( T | { T ?*?( T, T ); } ) 19 19 T square( T t ) { 20 20 return t * t; -
tests/zombies/structMember.cfa
rb6a8b31 rd95969a 66 66 S.T; 67 67 .S.T; 68 forall( otype S, otypeT ) struct W {68 forall( S, T ) struct W { 69 69 struct X {}; 70 70 }; -
tests/zombies/subrange.cfa
rb6a8b31 rd95969a 1 1 // A small context defining the notion of an ordered otype. (The standard 2 2 // library should probably contain a context for this purpose.) 3 trait ordered( otypeT) {3 trait ordered(T) { 4 4 int ?<?(T, T), ?<=?(T, T); 5 5 }; … … 7 7 // A subrange otype resembling an Ada subotype with a base otype and a range 8 8 // constraint. 9 otype subrange( otypebase_t | ordered(base_t), base_t low = 0, base_t high = 8) = base_t;9 otype subrange(base_t | ordered(base_t), base_t low = 0, base_t high = 8) = base_t; 10 10 11 11 // Note that subrange() can be applied to floating-point and pointer otypes, not … … 28 28 29 29 // Convenient access to subrange bounds, for instance for iteration: 30 forall ( otypeT, T low, T high)30 forall (T, T low, T high) 31 31 T lbound( subrange(T, low, high) v) { 32 32 return low; 33 33 } 34 34 35 forall ( otypeT, T low, T high)35 forall (T, T low, T high) 36 36 T hbound( subrange(T, low, high) v) { 37 37 return high; … … 44 44 // of exception handling here. Inlining allows the compiler to eliminate 45 45 // bounds checks. 46 forall ( otypeT | ordered(T), T low, T high)46 forall (T | ordered(T), T low, T high) 47 47 inline subrange(T, low, high) ?=?(subrange(T, low, high)* target, T source) { 48 48 if (low <= source && source <= high) *((T*)target) = source; … … 54 54 // compares range bounds so that the compiler can optimize checks away when the 55 55 // ranges are known to overlap. 56 forall ( otypeT | ordered(T), T t_low, T t_high, T s_low, T s_high)56 forall (T | ordered(T), T t_low, T t_high, T s_low, T s_high) 57 57 inline subrange(T, t_low, t_high) ?=?(subrange(T, t_low, t_high)* target, 58 58 subrange(T, s_low, s_high) source) { -
tests/zombies/twice.c
rb6a8b31 rd95969a 16 16 #include <fstream.hfa> 17 17 18 forall( otypeT | { T ?+?( T, T ); } )18 forall( T | { T ?+?( T, T ); } ) 19 19 T twice( const T t ) { 20 20 return t + t; -
tests/zombies/typeGenerator.cfa
rb6a8b31 rd95969a 1 context addable( otypeT ) {1 context addable( T ) { 2 2 T ?+?( T,T ); 3 3 T ?=?( T*, T); 4 4 }; 5 5 6 otype List1( otypeT | addable( T ) ) = struct { T data; List1( T ) *next; } *;6 otype List1( T | addable( T ) ) = struct { T data; List1( T ) *next; } *; 7 7 typedef List1( int ) ListOfIntegers; 8 8 //List1( int ) li; … … 11 11 [int] h( * List1( int ) p ); // new declaration syntax 12 12 13 struct( otypeT ) S2 { T i; }; // actual definition13 struct( T ) S2 { T i; }; // actual definition 14 14 struct( int ) S3 v1, *p; // expansion and instantiation 15 struct( otypeT )( int ) S24 { T i; } v2; // actual definition, expansion and instantiation16 struct( otypeT )( int ) { T i; } v2; // anonymous actual definition, expansion and instantiation15 struct( T )( int ) S24 { T i; } v2; // actual definition, expansion and instantiation 16 struct( T )( int ) { T i; } v2; // anonymous actual definition, expansion and instantiation 17 17 18 struct( otypeT | addable( T ) ) node { T data; struct( T ) node *next; };19 otype List( otypeT ) = struct( T ) node *;18 struct( T | addable( T ) ) node { T data; struct( T ) node *next; }; 19 otype List( T ) = struct( T ) node *; 20 20 List( int ) my_list; 21 21 -
tests/zombies/withStatement.cfa
rb6a8b31 rd95969a 54 54 } 55 55 56 forall( otypeT )56 forall( T ) 57 57 struct Box { 58 58 T x; 59 59 }; 60 60 61 forall( otypeT )61 forall( T ) 62 62 void ?{}( Box(T) & this ) with( this ) { // with clause in polymorphic function 63 63 x{}; … … 66 66 void print( int i ) { sout | i; } 67 67 68 forall( otypeT | { void print( T ); })68 forall( T | { void print( T ); }) 69 69 void foo( T t ) { 70 70 Box( T ) b = { t }; -
tests/zombies/wrapper/src/pointer.h
rb6a8b31 rd95969a 8 8 // type safe malloc / free 9 9 10 forall( otypeT)10 forall(T) 11 11 T* new() 12 12 { … … 16 16 } 17 17 18 forall( otypeT)18 forall(T) 19 19 void delete(T* p) 20 20 {
Note:
See TracChangeset
for help on using the changeset viewer.