Changes in / [f95634e:b7fd9daf]
- Files:
-
- 86 added
- 15 deleted
- 155 edited
-
benchmark/Cargo.toml.in (modified) (1 diff)
-
benchmark/Makefile.am (modified) (4 diffs)
-
benchmark/bench.h (modified) (1 diff)
-
benchmark/bench.rs (modified) (4 diffs)
-
benchmark/io/http/filecache.cfa (modified) (1 diff)
-
benchmark/io/http/http_ring.cpp (modified) (1 diff)
-
benchmark/io/http/main.cfa (modified) (3 diffs)
-
benchmark/io/http/options.cfa (modified) (5 diffs)
-
benchmark/io/http/options.hfa (modified) (1 diff)
-
benchmark/io/http/protocol.cfa (modified) (9 diffs)
-
benchmark/io/http/protocol.hfa (modified) (1 diff)
-
benchmark/io/http/worker.cfa (modified) (2 diffs)
-
benchmark/mutexStmt/JavaThread.java (added)
-
benchmark/mutexStmt/cpp1.cc (added)
-
benchmark/mutexStmt/cpp2.cc (added)
-
benchmark/mutexStmt/cpp4.cc (added)
-
benchmark/mutexStmt/cpp8.cc (added)
-
benchmark/mutexStmt/cppLock.hpp (added)
-
benchmark/mutexStmt/lock1.cfa (added)
-
benchmark/mutexStmt/lock2.cfa (added)
-
benchmark/mutexStmt/lock4.cfa (added)
-
benchmark/mutexStmt/lock8.cfa (added)
-
benchmark/mutexStmt/monitor1.cfa (added)
-
benchmark/mutexStmt/monitor2.cfa (added)
-
benchmark/mutexStmt/monitor4.cfa (added)
-
benchmark/mutexStmt/no_stmt_lock1.cfa (added)
-
benchmark/mutexStmt/no_stmt_lock2.cfa (added)
-
benchmark/mutexStmt/no_stmt_lock4.cfa (added)
-
benchmark/mutexStmt/no_stmt_lock8.cfa (added)
-
benchmark/readyQ/cycle.cpp (modified) (3 diffs)
-
benchmark/readyQ/cycle.go (modified) (2 diffs)
-
benchmark/readyQ/cycle.rs (modified) (1 diff)
-
benchmark/readyQ/locality.go (modified) (1 diff)
-
benchmark/readyQ/locality.rs (modified) (2 diffs)
-
benchmark/readyQ/transfer.cfa (modified) (3 diffs)
-
benchmark/readyQ/transfer.cpp (modified) (1 diff)
-
benchmark/readyQ/transfer.go (added)
-
benchmark/readyQ/transfer.rs (added)
-
benchmark/readyQ/yield.cfa (modified) (1 diff)
-
benchmark/readyQ/yield.cpp (modified) (1 diff)
-
benchmark/readyQ/yield.go (added)
-
benchmark/readyQ/yield.rs (added)
-
benchmark/rmit.py (modified) (7 diffs)
-
doc/theses/andrew_beach_MMath/Makefile (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/code/CondCatch.java (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/code/CrossCatch.java (deleted)
-
doc/theses/andrew_beach_MMath/code/CrossFinally.java (deleted)
-
doc/theses/andrew_beach_MMath/code/FixupEmpty.java (added)
-
doc/theses/andrew_beach_MMath/code/FixupOther.java (added)
-
doc/theses/andrew_beach_MMath/code/ThrowEmpty.java (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/code/ThrowFinally.java (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/code/ThrowOther.java (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/code/TryCatch.java (added)
-
doc/theses/andrew_beach_MMath/code/TryFinally.java (added)
-
doc/theses/andrew_beach_MMath/code/cond-catch.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/cond-catch.cpp (modified) (4 diffs)
-
doc/theses/andrew_beach_MMath/code/cond-catch.py (added)
-
doc/theses/andrew_beach_MMath/code/cond-fixup.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/cond_catch.py (deleted)
-
doc/theses/andrew_beach_MMath/code/cross-catch.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/cross-catch.cpp (deleted)
-
doc/theses/andrew_beach_MMath/code/cross-finally.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/cross-resume.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/cross_catch.py (deleted)
-
doc/theses/andrew_beach_MMath/code/cross_finally.py (deleted)
-
doc/theses/andrew_beach_MMath/code/fixup-empty-f.cfa (added)
-
doc/theses/andrew_beach_MMath/code/fixup-empty-r.cfa (added)
-
doc/theses/andrew_beach_MMath/code/fixup-empty.cpp (added)
-
doc/theses/andrew_beach_MMath/code/fixup-empty.py (added)
-
doc/theses/andrew_beach_MMath/code/fixup-other-f.cfa (added)
-
doc/theses/andrew_beach_MMath/code/fixup-other-r.cfa (added)
-
doc/theses/andrew_beach_MMath/code/fixup-other.cpp (added)
-
doc/theses/andrew_beach_MMath/code/fixup-other.py (added)
-
doc/theses/andrew_beach_MMath/code/resume-detor.cfa (modified) (4 diffs)
-
doc/theses/andrew_beach_MMath/code/resume-empty.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/resume-finally.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/resume-other.cfa (modified) (4 diffs)
-
doc/theses/andrew_beach_MMath/code/run.sh (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/code/test.sh (modified) (5 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-detor.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-detor.cpp (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-empty.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-empty.cpp (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-empty.py (added)
-
doc/theses/andrew_beach_MMath/code/throw-finally.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-finally.py (added)
-
doc/theses/andrew_beach_MMath/code/throw-other.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-other.cpp (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-other.py (added)
-
doc/theses/andrew_beach_MMath/code/throw-with.py (added)
-
doc/theses/andrew_beach_MMath/code/throw_empty.py (deleted)
-
doc/theses/andrew_beach_MMath/code/throw_finally.py (deleted)
-
doc/theses/andrew_beach_MMath/code/throw_other.py (deleted)
-
doc/theses/andrew_beach_MMath/code/throw_with.py (deleted)
-
doc/theses/andrew_beach_MMath/code/try-catch.cfa (added)
-
doc/theses/andrew_beach_MMath/code/try-catch.cpp (added)
-
doc/theses/andrew_beach_MMath/code/try-catch.py (added)
-
doc/theses/andrew_beach_MMath/code/try-finally.cfa (added)
-
doc/theses/andrew_beach_MMath/code/try-finally.py (added)
-
doc/theses/andrew_beach_MMath/code/try-resume.cfa (added)
-
doc/theses/andrew_beach_MMath/conclusion.tex (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/exception-layout.fig (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/existing.tex (modified) (18 diffs)
-
doc/theses/andrew_beach_MMath/features.tex (modified) (36 diffs)
-
doc/theses/andrew_beach_MMath/future.tex (modified) (8 diffs)
-
doc/theses/andrew_beach_MMath/implement.tex (modified) (38 diffs)
-
doc/theses/andrew_beach_MMath/intro.tex (modified) (4 diffs)
-
doc/theses/andrew_beach_MMath/performance.tex (modified) (7 diffs)
-
doc/theses/andrew_beach_MMath/resumhandle.fig (added)
-
doc/theses/andrew_beach_MMath/resumption-marking.fig (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/termhandle.fig (added)
-
doc/theses/andrew_beach_MMath/uw-ethesis-frontpgs.tex (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/uw-ethesis.bib (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/uw-ethesis.tex (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/virtual-tree.fig (added)
-
doc/theses/andrew_beach_MMath/vtable-layout.fig (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/vtable.fig (deleted)
-
doc/theses/mubeen_zulfiqar_MMath/allocator.tex (modified) (21 diffs)
-
doc/theses/mubeen_zulfiqar_MMath/background.tex (modified) (1 diff)
-
doc/theses/mubeen_zulfiqar_MMath/benchmarks.tex (modified) (1 diff)
-
doc/theses/mubeen_zulfiqar_MMath/intro.tex (modified) (2 diffs)
-
doc/theses/mubeen_zulfiqar_MMath/performance.tex (modified) (1 diff)
-
doc/theses/mubeen_zulfiqar_MMath/thesis.tex (deleted)
-
doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.bib (modified) (1 diff)
-
doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.tex (modified) (3 diffs)
-
doc/theses/thierry_delisle_PhD/.gitignore (modified) (1 diff)
-
doc/theses/thierry_delisle_PhD/thesis/Makefile (modified) (2 diffs)
-
doc/theses/thierry_delisle_PhD/thesis/fig/cycle.fig (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/eval_macro.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/thesis.tex (modified) (6 diffs)
-
doc/user/user.tex (modified) (7 diffs)
-
example/cpu.cfa (added)
-
example/maybe-await.rs (added)
-
example/unnecessary-arc.rs (added)
-
libcfa/prelude/bootloader.cf (modified) (2 diffs)
-
libcfa/prelude/builtins.c (modified) (2 diffs)
-
libcfa/src/Makefile.am (modified) (6 diffs)
-
libcfa/src/concurrency/clib/cfathread.cfa (modified) (9 diffs)
-
libcfa/src/concurrency/invoke.h (modified) (2 diffs)
-
libcfa/src/concurrency/io.cfa (modified) (4 diffs)
-
libcfa/src/concurrency/io/types.hfa (modified) (1 diff)
-
libcfa/src/concurrency/kernel.cfa (modified) (19 diffs)
-
libcfa/src/concurrency/kernel.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/kernel/fwd.hfa (modified) (1 diff)
-
libcfa/src/concurrency/kernel/startup.cfa (modified) (6 diffs)
-
libcfa/src/concurrency/kernel_private.hfa (modified) (3 diffs)
-
libcfa/src/concurrency/locks.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/monitor.cfa (modified) (1 diff)
-
libcfa/src/concurrency/monitor.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/mutex_stmt.hfa (added)
-
libcfa/src/concurrency/ready_queue.cfa (modified) (15 diffs)
-
libcfa/src/concurrency/ready_subqueue.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/stats.cfa (modified) (3 diffs)
-
libcfa/src/concurrency/stats.hfa (modified) (1 diff)
-
libcfa/src/concurrency/thread.cfa (modified) (4 diffs)
-
libcfa/src/containers/string.cfa (added)
-
libcfa/src/containers/string.hfa (added)
-
libcfa/src/containers/string_res.cfa (added)
-
libcfa/src/containers/string_res.hfa (added)
-
libcfa/src/device/cpu.cfa (modified) (2 diffs)
-
libcfa/src/device/cpu.hfa (modified) (1 diff)
-
libcfa/src/fstream.cfa (modified) (14 diffs)
-
libcfa/src/fstream.hfa (modified) (6 diffs)
-
libcfa/src/heap.cfa (modified) (1 diff)
-
libcfa/src/interpose.cfa (modified) (1 diff)
-
libcfa/src/iostream.cfa (modified) (4 diffs)
-
libcfa/src/iostream.hfa (modified) (6 diffs)
-
libcfa/src/memory.cfa (modified) (1 diff)
-
libcfa/src/memory.hfa (modified) (1 diff)
-
libcfa/src/parseconfig.cfa (added)
-
libcfa/src/parseconfig.hfa (added)
-
libcfa/src/strstream.cfa (modified) (5 diffs)
-
libcfa/src/strstream.hfa (modified) (2 diffs)
-
src/AST/Decl.hpp (modified) (1 diff)
-
src/AST/Pass.hpp (modified) (2 diffs)
-
src/AST/Pass.impl.hpp (modified) (1 diff)
-
src/AST/Pass.proto.hpp (modified) (1 diff)
-
src/AST/Stmt.hpp (modified) (1 diff)
-
src/AST/TranslationUnit.hpp (modified) (1 diff)
-
src/AST/porting.md (modified) (1 diff)
-
src/CodeGen/FixMain.cc (modified) (4 diffs)
-
src/CodeGen/FixMain.h (modified) (2 diffs)
-
src/CodeGen/FixNames.cc (modified) (6 diffs)
-
src/CodeGen/FixNames.h (modified) (2 diffs)
-
src/CodeTools/DeclStats.cc (modified) (3 diffs)
-
src/Common/DeclStats.cpp (added)
-
src/Common/DeclStats.hpp (added)
-
src/Common/ResolvProtoDump.cpp (added)
-
src/Common/ResolvProtoDump.hpp (added)
-
src/Common/module.mk (modified) (2 diffs)
-
src/Concurrency/Keywords.cc (modified) (10 diffs)
-
src/ControlStruct/ExceptTranslate.cc (modified) (1 diff)
-
src/ControlStruct/ExceptTranslate.h (modified) (2 diffs)
-
src/ControlStruct/ExceptTranslateNew.cpp (added)
-
src/ControlStruct/FixLabels.cpp (added)
-
src/ControlStruct/FixLabels.hpp (added)
-
src/ControlStruct/LabelGenerator.cc (modified) (4 diffs)
-
src/ControlStruct/LabelGenerator.h (modified) (2 diffs)
-
src/ControlStruct/MultiLevelExit.cpp (added)
-
src/ControlStruct/MultiLevelExit.hpp (added)
-
src/ControlStruct/module.mk (modified) (2 diffs)
-
src/InitTweak/GenInit.cc (modified) (3 diffs)
-
src/InitTweak/GenInit.h (modified) (2 diffs)
-
src/MakeLibCfa.h (modified) (1 diff)
-
src/MakeLibCfaNew.cpp (added)
-
src/Makefile.am (modified) (1 diff)
-
src/Parser/parser.yy (modified) (17 diffs)
-
src/ResolvExpr/CandidatePrinter.cpp (added)
-
src/ResolvExpr/CandidatePrinter.hpp (added)
-
src/ResolvExpr/module.mk (modified) (1 diff)
-
src/Tuples/TupleExpansionNew.cpp (modified) (2 diffs)
-
src/Tuples/Tuples.h (modified) (2 diffs)
-
src/Tuples/module.mk (modified) (1 diff)
-
src/main.cc (modified) (8 diffs)
-
tests/.expect/declarationSpecifier.x64.txt (modified) (2 diffs)
-
tests/.expect/declarationSpecifier.x86.txt (modified) (2 diffs)
-
tests/.expect/gccExtensions.x64.txt (modified) (2 diffs)
-
tests/.expect/gccExtensions.x86.txt (modified) (2 diffs)
-
tests/.expect/parseconfig.txt (added)
-
tests/.in/parseconfig-all.txt (added)
-
tests/.in/parseconfig-errors.txt (added)
-
tests/.in/parseconfig-missing.txt (added)
-
tests/Makefile.am (modified) (1 diff)
-
tests/collections/.expect/string-api-coverage.txt (added)
-
tests/collections/.expect/string-gc.txt (added)
-
tests/collections/.expect/string-overwrite.txt (added)
-
tests/collections/string-api-coverage.cfa (added)
-
tests/collections/string-gc.cfa (added)
-
tests/collections/string-overwrite.cfa (added)
-
tests/concurrent/mutexstmt/.expect/locks.txt (added)
-
tests/concurrent/mutexstmt/locks.cfa (modified) (2 diffs)
-
tests/concurrent/mutexstmt/monitors.cfa (modified) (2 diffs)
-
tests/concurrent/semaphore.cfa (modified) (2 diffs)
-
tests/concurrent/sleep.cfa (modified) (3 diffs)
-
tests/exceptions/.expect/type-check.txt (modified) (1 diff)
-
tests/exceptions/cancel/coroutine.cfa (modified) (1 diff)
-
tests/exceptions/cancel/thread.cfa (modified) (1 diff)
-
tests/exceptions/conditional.cfa (modified) (1 diff)
-
tests/exceptions/data-except.cfa (modified) (1 diff)
-
tests/exceptions/defaults.cfa (modified) (4 diffs)
-
tests/exceptions/finally.cfa (modified) (1 diff)
-
tests/exceptions/interact.cfa (modified) (1 diff)
-
tests/exceptions/polymorphic.cfa (modified) (2 diffs)
-
tests/exceptions/resume.cfa (modified) (1 diff)
-
tests/exceptions/terminate.cfa (modified) (1 diff)
-
tests/exceptions/trash.cfa (modified) (1 diff)
-
tests/exceptions/type-check.cfa (modified) (1 diff)
-
tests/io/io-acquire.cfa (modified) (5 diffs)
-
tests/linking/io-acquire.cfa (modified) (1 diff)
-
tests/parseconfig.cfa (added)
-
tests/pybin/test_run.py (modified) (1 diff)
-
tests/test.py (modified) (6 diffs)
-
tools/perf/png.py (added)
-
tools/perf/process_stat_array.py (modified) (5 diffs)
-
tools/perf/sample.py (added)
Legend:
- Unmodified
- Added
- Removed
-
benchmark/Cargo.toml.in
rf95634e rb7fd9daf 6 6 7 7 [[bin]] 8 name = " cycle-tokio"8 name = "rdq-cycle-tokio" 9 9 path = "@abs_srcdir@/readyQ/cycle.rs" 10 10 11 11 [[bin]] 12 name = " locality-tokio"12 name = "rdq-locality-tokio" 13 13 path = "@abs_srcdir@/readyQ/locality.rs" 14 15 [[bin]] 16 name = "rdq-transfer-tokio" 17 path = "@abs_srcdir@/readyQ/transfer.rs" 18 19 [[bin]] 20 name = "rdq-yield-tokio" 21 path = "@abs_srcdir@/readyQ/yield.rs" 14 22 15 23 [features] -
benchmark/Makefile.am
rf95634e rb7fd9daf 21 21 include $(top_srcdir)/tools/build/cfa.make 22 22 23 AM_CFLAGS = -O 2-Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror23 AM_CFLAGS = -O3 -Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror 24 24 AM_CFAFLAGS = -quiet -nodebug 25 25 AM_UPPFLAGS = -quiet -nodebug -multi -std=c++14 … … 197 197 $(srcdir)/fixcsv.sh $@ 198 198 199 # use --no-print-directory to generate csv appropriately 200 mutexStmt.csv: 201 echo "building $@" 202 echo "1-lock,2-lock,4-lock,8-lock,1-no-stmt-lock,2-no-stmt-lock,4-no-stmt-lock,8-no-stmt-lock,1-monitor,2-monitor,4-monitor" > $@ 203 +make mutexStmt-lock1.runquiet >> $@ && echo -n ',' >> $@ 204 +make mutexStmt-lock2.runquiet >> $@ && echo -n ',' >> $@ 205 +make mutexStmt-lock4.runquiet >> $@ && echo -n ',' >> $@ 206 +make mutexStmt-lock8.runquiet >> $@ && echo -n ',' >> $@ 207 +make mutexStmt-no-stmt-lock1.runquiet >> $@ && echo -n ',' >> $@ 208 +make mutexStmt-no-stmt-lock2.runquiet >> $@ && echo -n ',' >> $@ 209 +make mutexStmt-no-stmt-lock4.runquiet >> $@ && echo -n ',' >> $@ 210 +make mutexStmt-no-stmt-lock8.runquiet >> $@ && echo -n ',' >> $@ 211 +make mutexStmt-monitor1.runquiet >> $@ && echo -n ',' >> $@ 212 +make mutexStmt-monitor2.runquiet >> $@ && echo -n ',' >> $@ 213 +make mutexStmt-monitor4.runquiet >> $@ 214 $(srcdir)/fixcsv.sh $@ 215 199 216 schedint.csv: 200 217 echo "building $@" … … 357 374 ## ========================================================================================================= 358 375 376 mutexStmt$(EXEEXT) : \ 377 mutexStmt-cpp1.run \ 378 mutexStmt-cpp2.run \ 379 mutexStmt-cpp4.run \ 380 mutexStmt-cpp8.run \ 381 mutexStmt-java.run \ 382 mutexStmt-lock1.run \ 383 mutexStmt-lock2.run \ 384 mutexStmt-lock4.run \ 385 mutexStmt-lock8.run \ 386 mutexStmt-no-stmt-lock1.run \ 387 mutexStmt-no-stmt-lock2.run \ 388 mutexStmt-no-stmt-lock4.run \ 389 mutexStmt-no-stmt-lock8.run \ 390 mutexStmt-monitor1.run \ 391 mutexStmt-monitor2.run \ 392 mutexStmt-monitor4.run 393 394 mutexStmt-lock1$(EXEEXT): 395 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock1.cfa 396 397 mutexStmt-lock2$(EXEEXT): 398 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock2.cfa 399 400 mutexStmt-lock4$(EXEEXT): 401 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock4.cfa 402 403 mutexStmt-lock8$(EXEEXT): 404 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock8.cfa 405 406 mutexStmt-cpp1$(EXEEXT): 407 $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp1.cc 408 409 mutexStmt-cpp2$(EXEEXT): 410 $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp2.cc 411 412 mutexStmt-cpp4$(EXEEXT): 413 $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp4.cc 414 415 mutexStmt-cpp8$(EXEEXT): 416 $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp8.cc 417 418 mutexStmt-monitor1$(EXEEXT): 419 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor1.cfa 420 421 mutexStmt-monitor2$(EXEEXT): 422 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor2.cfa 423 424 mutexStmt-monitor4$(EXEEXT): 425 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor4.cfa 426 427 mutexStmt-no-stmt-lock1$(EXEEXT): 428 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock1.cfa 429 430 mutexStmt-no-stmt-lock2$(EXEEXT): 431 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock2.cfa 432 433 mutexStmt-no-stmt-lock4$(EXEEXT): 434 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock4.cfa 435 436 mutexStmt-no-stmt-lock8$(EXEEXT): 437 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock8.cfa 438 439 mutexStmt-java$(EXEEXT): 440 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/mutexStmt/JavaThread.java 441 echo "#!/bin/sh" > a.out 442 echo "java JavaThread \"$$""@\"" >> a.out 443 chmod a+x a.out 444 445 ## ========================================================================================================= 446 359 447 schedint$(EXEEXT) : \ 360 448 schedint-cfa1.run \ … … 524 612 ## ========================================================================================================= 525 613 526 %-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs 527 cd $(builddir) && cargo build --release 528 cp $(builddir)/target/release/$(basename $@) $@ 614 RDQBENCHES = \ 615 rdq-cycle-cfa \ 616 rdq-cycle-tokio \ 617 rdq-cycle-go \ 618 rdq-cycle-fibre \ 619 rdq-yield-cfa \ 620 rdq-yield-tokio \ 621 rdq-yield-go \ 622 rdq-yield-fibre \ 623 rdq-locality-cfa \ 624 rdq-locality-tokio \ 625 rdq-locality-go \ 626 rdq-locality-fibre \ 627 rdq-transfer-cfa \ 628 rdq-transfer-tokio \ 629 rdq-transfer-go \ 630 rdq-transfer-fibre 631 632 rdq-benches: 633 +make $(RDQBENCHES) 634 635 clean-rdq-benches: 636 rm -rf $(RDQBENCHES) $(builddir)/target go.mod 637 638 rdq-%-tokio$(EXEEXT): $(builddir)/target/release/rdq-%-tokio$(EXEEXT) 639 $(BENCH_V_RUSTC)cp $(builddir)/target/release/$(basename $@) $@ 640 641 $(builddir)/target/release/rdq-%-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs 642 $(BENCH_V_RUSTC)cd $(builddir) && cargo build --release 643 644 rdq-%-cfa$(EXEEXT): $(srcdir)/readyQ/%.cfa $(srcdir)/readyQ/rq_bench.hfa 645 $(BENCH_V_CFA)$(CFACOMPILE) $< -o $@ 646 647 go.mod: 648 touch $@ 649 go mod edit -module=rdq.bench 650 go get golang.org/x/sync/semaphore 651 go get golang.org/x/text/language 652 go get golang.org/x/text/message 653 654 rdq-%-go$(EXEEXT): $(srcdir)/readyQ/%.go $(srcdir)/readyQ/bench.go go.mod 655 $(BENCH_V_GOC)go build -o $@ $< $(srcdir)/readyQ/bench.go 656 657 rdq-%-fibre$(EXEEXT): $(srcdir)/readyQ/%.cpp 658 $(BENCH_V_CXX)$(CXXCOMPILE) $< -o $@ -lfibre -std=c++17 $(AM_CFLAGS) 659 660 # ## ========================================================================================================= 661 662 CLEANFILES = $(RDQBENCHES) go.mod go.sum 663 664 clean-local: 665 -rm -rf target -
benchmark/bench.h
rf95634e rb7fd9daf 21 21 return 1000000000LL * ts.tv_sec + ts.tv_nsec; 22 22 } // bench_time 23 24 25 #if defined(__cforall) 26 struct test_spinlock { 27 volatile bool lock; 28 }; 29 30 static inline void lock( test_spinlock & this ) { 31 for ( ;; ) { 32 if ( (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0) ) break; 33 } 34 } 35 36 static inline void unlock( test_spinlock & this ) { 37 __atomic_clear( &this.lock, __ATOMIC_RELEASE ); 38 } 39 #endif 23 40 24 41 #ifndef BENCH_N -
benchmark/bench.rs
rf95634e rb7fd9daf 1 1 use std::io::{self, Write}; 2 use std::option; 2 3 use std::sync::atomic::{AtomicU64, AtomicBool, Ordering}; 3 4 use std::time::{Instant,Duration}; 5 use std::u128; 4 6 5 7 use clap::{Arg, ArgMatches}; … … 27 29 28 30 impl BenchData { 29 pub fn new(options: ArgMatches, nthreads: usize ) -> BenchData {31 pub fn new(options: ArgMatches, nthreads: usize, default_it: option::Option<u64>) -> BenchData { 30 32 let (clock_mode, stop_count, duration) = if options.is_present("iterations") { 31 33 (false, 32 34 options.value_of("iterations").unwrap().parse::<u64>().unwrap(), 35 -1.0) 36 } else if !default_it.is_none() { 37 (false, 38 default_it.unwrap(), 33 39 -1.0) 34 40 } else { … … 48 54 } 49 55 56 #[allow(dead_code)] 50 57 pub async fn wait(&self, start: &Instant) -> Duration{ 51 58 loop { … … 69 76 } 70 77 78 // ================================================== 79 pub fn _lehmer64( state: &mut u128 ) -> u64 { 80 *state = state.wrapping_mul(0xda942042e4dd58b5); 81 return (*state >> 64) as u64; 82 } -
benchmark/io/http/filecache.cfa
rf95634e rb7fd9daf 185 185 sout | "Filled cache from path \"" | path | "\" with" | fcount | "files"; 186 186 if( conflicts > 0 ) { 187 sout | "Found" | conflicts | "conflicts (s eed: " | options.file_cache.hash_seed | ")";187 sout | "Found" | conflicts | "conflicts (size: " | file_cache.size | ", seed: " | options.file_cache.hash_seed | ")"; 188 188 #if defined(REJECT_CONFLICTS) 189 189 abort("Conflicts found in the cache"); -
benchmark/io/http/http_ring.cpp
rf95634e rb7fd9daf 118 118 // Get a fix reply based on the return code 119 119 const char * http_msgs[] = { 120 "HTTP/1.1 200 OK\r\nServer: Htt oForall\r\nContent-Type: text/plain\r\nContent-Length: 15\r\nConnection: keep-alive\r\n\r\nHello, World!\r\n",121 "HTTP/1.1 400 Bad Request\r\nServer: Htt oForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",122 "HTTP/1.1 404 Not Found\r\nServer: Htt oForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",123 "HTTP/1.1 405 Method Not \r\nServer: Htt oForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",124 "HTTP/1.1 408 Request Timeout\r\nServer: Htt oForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",125 "HTTP/1.1 413 Payload Too Large\r\nServer: Htt oForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",126 "HTTP/1.1 414 URI Too Long\r\nServer: Htt oForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",120 "HTTP/1.1 200 OK\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 15\r\nConnection: keep-alive\r\n\r\nHello, World!\r\n", 121 "HTTP/1.1 400 Bad Request\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 122 "HTTP/1.1 404 Not Found\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 123 "HTTP/1.1 405 Method Not \r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 124 "HTTP/1.1 408 Request Timeout\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 125 "HTTP/1.1 413 Payload Too Large\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 126 "HTTP/1.1 414 URI Too Long\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 127 127 }; 128 128 static_assert( KNOWN_CODES == (sizeof(http_msgs) / sizeof(http_msgs[0])) ); -
benchmark/io/http/main.cfa
rf95634e rb7fd9daf 190 190 init_protocol(); 191 191 { 192 Worker workers[options.clopts.nworkers];192 Worker * workers = anew(options.clopts.nworkers); 193 193 for(i; options.clopts.nworkers) { 194 194 // if( options.file_cache.fixed_fds ) { … … 212 212 } 213 213 sout | nl; 214 if(!options.interactive) park(); 214 215 { 215 216 char buffer[128]; … … 249 250 250 251 sout | "Stopping connection threads..." | nonl; flush( sout ); 252 adelete(workers); 251 253 } 252 254 sout | "done"; -
benchmark/io/http/options.cfa
rf95634e rb7fd9daf 21 21 false, // log 22 22 false, // stats 23 true, // interactive 24 0, // redirect 25 0, // redirect 23 26 24 27 { // file_cache … … 52 55 // bool sqkpoll = false; 53 56 // bool iokpoll = false; 54 unsigned nentries = 16;57 unsigned nentries = 0; 55 58 bool isolate = false; 56 59 … … 62 65 {'\0', "isolate", "Create one cluster per processor", isolate, parse_settrue}, 63 66 {'\0', "log", "Enable logs", options.log, parse_settrue}, 67 {'\0', "sout", "Redirect standard out to file", options.reopen_stdout}, 68 {'\0', "serr", "Redirect standard error to file", options.reopen_stderr}, 64 69 {'\0', "stats", "Enable statistics", options.stats, parse_settrue}, 70 {'\0', "shell", "Disable interactive mode", options.interactive, parse_setfalse}, 65 71 {'\0', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog}, 66 72 {'\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}, … … 79 85 parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left ); 80 86 81 if( !is_pow2(nentries) ) {87 if( nentries != 0 && !is_pow2(nentries) ) { 82 88 unsigned v = nentries; 83 89 v--; … … 131 137 132 138 options.file_cache.path = path; 139 140 if( options.reopen_stdout && options.reopen_stderr && 0 == strcmp(options.reopen_stdout, options.reopen_stderr) ) { 141 serr | "Redirect sout and serr to the same file is not supported"; 142 exit(EXIT_FAILURE); 143 } 144 145 if( options.reopen_stdout ) { 146 sout | "redirecting sout to '" | options.reopen_stdout | "'"; 147 FILE * ret = freopen( options.reopen_stdout, "w", stdout); 148 if( ret == 0p ) { 149 serr | "Failed to redirect sout to '" | options.reopen_stdout | "'"; 150 exit(EXIT_FAILURE); 151 } 152 } 153 154 if( options.reopen_stderr ) { 155 sout | "redirecting serr to '" | options.reopen_stderr | "'"; 156 FILE * ret = freopen( options.reopen_stderr, "w", stderr); 157 if( ret == 0p ) { 158 serr | "Failed to redirect serr to '" | options.reopen_stderr | "'"; 159 exit(EXIT_FAILURE); 160 } 161 } 133 162 } -
benchmark/io/http/options.hfa
rf95634e rb7fd9daf 10 10 bool log; 11 11 bool stats; 12 bool interactive; 13 const char * reopen_stdout; 14 const char * reopen_stderr; 12 15 13 16 struct { -
benchmark/io/http/protocol.cfa
rf95634e rb7fd9daf 11 11 #include <fstream.hfa> 12 12 #include <iofwd.hfa> 13 #include <io/types.hfa> 14 #include <mutex_stmt.hfa> 13 15 14 16 #include <assert.h> … … 26 28 #define PLAINTEXT_MEMCPY 27 29 #define PLAINTEXT_NOCOPY 30 #define LINKED_IO 28 31 29 32 struct https_msg_str { … … 53 56 } 54 57 55 static inline int answer( int fd, const char * it, int len ) {58 static inline int answer( int fd, const char * it, int len ) { 56 59 while(len > 0) { 57 60 // Call write 58 61 int ret = cfa_send(fd, it, len, 0, CFA_IO_LAZY); 59 62 if( ret < 0 ) { 60 if( errno == ECONNRESET || errno == EPIPE ) return -ECONNRESET;63 if( errno == ECONNRESET || errno == EPIPE ) { close(fd); return -ECONNRESET; } 61 64 if( errno == EAGAIN || errno == EWOULDBLOCK) return -EAGAIN; 62 65 … … 77 80 } 78 81 79 int answer_header( int fd, size_t size ) { 80 char buffer[512]; 81 char * it = buffer; 82 static int fill_header(char * it, size_t size) { 82 83 memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len); 83 84 it += http_msgs[OK200]->len; 84 85 int len = http_msgs[OK200]->len; 85 86 len += snprintf(it, 512 - len, "%d \n\n", size); 87 return len; 88 } 89 90 static int answer_header( int fd, size_t size ) { 91 char buffer[512]; 92 int len = fill_header(buffer, size); 86 93 return answer( fd, buffer, len ); 87 94 } … … 135 142 } 136 143 137 138 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len) { 139 char * it = buffer; 140 size_t count = len - 1; 141 int rlen = 0; 142 READ: 143 for() { 144 int ret = cfa_recv(fd, (void*)it, count, 0, CFA_IO_LAZY); 145 // int ret = read(fd, (void*)it, count); 146 if(ret == 0 ) return [OK200, true, 0, 0]; 147 if(ret < 0 ) { 148 if( errno == EAGAIN || errno == EWOULDBLOCK) continue READ; 149 if( errno == ECONNRESET ) return [E408, true, 0, 0]; 150 if( errno == EPIPE ) return [E408, true, 0, 0]; 151 abort( "read error: (%d) %s\n", (int)errno, strerror(errno) ); 152 } 153 it[ret + 1] = '\0'; 154 rlen += ret; 155 156 if( strstr( it, "\r\n\r\n" ) ) break; 157 158 it += ret; 159 count -= ret; 160 161 if( count < 1 ) return [E414, false, 0, 0]; 162 } 163 164 if( options.log ) { 165 write(sout, buffer, rlen); 166 sout | nl; 167 } 168 169 it = buffer; 170 int ret = memcmp(it, "GET /", 5); 171 if( ret != 0 ) return [E400, false, 0, 0]; 172 it += 5; 173 174 char * end = strstr( it, " " ); 175 return [OK200, false, it, end - it]; 176 } 177 178 int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) { 144 static int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) { 179 145 unsigned sflags = SPLICE_F_MOVE; // | SPLICE_F_MORE; 180 146 off_t offset = 0; … … 207 173 } 208 174 175 static void zero_sqe(struct io_uring_sqe * sqe) { 176 sqe->flags = 0; 177 sqe->ioprio = 0; 178 sqe->fd = 0; 179 sqe->off = 0; 180 sqe->addr = 0; 181 sqe->len = 0; 182 sqe->fsync_flags = 0; 183 sqe->__pad2[0] = 0; 184 sqe->__pad2[1] = 0; 185 sqe->__pad2[2] = 0; 186 sqe->fd = 0; 187 sqe->off = 0; 188 sqe->addr = 0; 189 sqe->len = 0; 190 } 191 192 enum FSM_STATE { 193 Initial, 194 Retry, 195 Error, 196 Done, 197 }; 198 199 struct FSM_Result { 200 FSM_STATE state; 201 int error; 202 }; 203 204 static inline void ?{}(FSM_Result & this) { this.state = Initial; this.error = 0; } 205 static inline bool is_error(FSM_Result & this) { return Error == this.state; } 206 static inline bool is_done(FSM_Result & this) { return Done == this.state; } 207 208 static inline int error(FSM_Result & this, int error) { 209 this.error = error; 210 this.state = Error; 211 return error; 212 } 213 214 static inline int done(FSM_Result & this) { 215 this.state = Done; 216 return 0; 217 } 218 219 static inline int retry(FSM_Result & this) { 220 this.state = Retry; 221 return 0; 222 } 223 224 static inline int need(FSM_Result & this) { 225 switch(this.state) { 226 case Initial: 227 case Retry: 228 return 1; 229 case Error: 230 if(this.error == 0) mutex(serr) serr | "State marked error but code is 0"; 231 case Done: 232 return 0; 233 } 234 } 235 236 // Generator that handles sending the header 237 generator header_g { 238 io_future_t f; 239 const char * next; 240 int fd; size_t len; 241 FSM_Result res; 242 }; 243 244 static inline void ?{}(header_g & this, int fd, const char * it, size_t len ) { 245 this.next = it; 246 this.fd = fd; 247 this.len = len; 248 } 249 250 static inline void fill(header_g & this, struct io_uring_sqe * sqe) { 251 zero_sqe(sqe); 252 sqe->opcode = IORING_OP_SEND; 253 sqe->user_data = (uintptr_t)&this.f; 254 sqe->flags = IOSQE_IO_LINK; 255 sqe->fd = this.fd; 256 sqe->addr = (uintptr_t)this.next; 257 sqe->len = this.len; 258 } 259 260 static inline int error(header_g & this, int error) { 261 int ret = close(this.fd); 262 if( ret != 0 ) { 263 mutex(serr) serr | "Failed to close fd" | errno; 264 } 265 return error(this.res, error); 266 } 267 268 static inline int wait_and_process(header_g & this) { 269 wait(this.f); 270 271 // Did something crazy happen? 272 if(this.f.result > this.len) { 273 mutex(serr) serr | "HEADER sent too much!"; 274 return error(this, -ERANGE); 275 } 276 277 // Something failed? 278 if(this.f.result < 0) { 279 int error = -this.f.result; 280 if( error == ECONNRESET ) return error(this, -ECONNRESET); 281 if( error == EPIPE ) return error(this, -EPIPE); 282 if( error == ECANCELED ) { 283 mutex(serr) serr | "HEADER was cancelled, WTF!"; 284 return error(this, -ECONNRESET); 285 } 286 if( error == EAGAIN || error == EWOULDBLOCK) { 287 mutex(serr) serr | "HEADER got eagain, WTF!"; 288 return error(this, -ECONNRESET); 289 } 290 } 291 292 // Done? 293 if(this.f.result == this.len) { 294 return done(this.res); 295 } 296 297 // It must be a Short read 298 this.len -= this.f.result; 299 this.next += this.f.result; 300 reset(this.f); 301 return retry(this.res); 302 } 303 304 // Generator that handles splicing in a file 305 struct splice_in_t { 306 io_future_t f; 307 int fd; int pipe; size_t len; off_t off; 308 FSM_Result res; 309 }; 310 311 static inline void ?{}(splice_in_t & this, int fd, int pipe, size_t len) { 312 this.fd = fd; 313 this.pipe = pipe; 314 this.len = len; 315 this.off = 0; 316 } 317 318 static inline void fill(splice_in_t & this, struct io_uring_sqe * sqe) { 319 zero_sqe(sqe); 320 sqe->opcode = IORING_OP_SPLICE; 321 sqe->user_data = (uintptr_t)&this.f; 322 sqe->flags = 0; 323 sqe->splice_fd_in = this.fd; 324 sqe->splice_off_in = this.off; 325 sqe->fd = this.pipe; 326 sqe->off = (__u64)-1; 327 sqe->len = this.len; 328 sqe->splice_flags = SPLICE_F_MOVE; 329 } 330 331 static inline int wait_and_process(splice_in_t & this) { 332 wait(this.f); 333 334 // Did something crazy happen? 335 if(this.f.result > this.len) { 336 mutex(serr) serr | "SPLICE IN spliced too much!"; 337 return error(this.res, -ERANGE); 338 } 339 340 // Something failed? 341 if(this.f.result < 0) { 342 int error = -this.f.result; 343 if( error == ECONNRESET ) return error(this.res, -ECONNRESET); 344 if( error == EPIPE ) return error(this.res, -EPIPE); 345 if( error == ECANCELED ) { 346 mutex(serr) serr | "SPLICE IN was cancelled, WTF!"; 347 return error(this.res, -ECONNRESET); 348 } 349 if( error == EAGAIN || error == EWOULDBLOCK) { 350 mutex(serr) serr | "SPLICE IN got eagain, WTF!"; 351 return error(this.res, -ECONNRESET); 352 } 353 } 354 355 // Done? 356 if(this.f.result == this.len) { 357 return done(this.res); 358 } 359 360 // It must be a Short read 361 this.len -= this.f.result; 362 this.off += this.f.result; 363 reset(this.f); 364 return retry(this.res); 365 } 366 367 generator splice_out_g { 368 io_future_t f; 369 int pipe; int fd; size_t len; 370 FSM_Result res; 371 }; 372 373 static inline void ?{}(splice_out_g & this, int pipe, int fd, size_t len) { 374 this.pipe = pipe; 375 this.fd = fd; 376 this.len = len; 377 } 378 379 static inline void fill(splice_out_g & this, struct io_uring_sqe * sqe) { 380 zero_sqe(sqe); 381 sqe->opcode = IORING_OP_SPLICE; 382 sqe->user_data = (uintptr_t)&this.f; 383 sqe->flags = 0; 384 sqe->splice_fd_in = this.pipe; 385 sqe->splice_off_in = (__u64)-1; 386 sqe->fd = this.fd; 387 sqe->off = (__u64)-1; 388 sqe->len = this.len; 389 sqe->splice_flags = SPLICE_F_MOVE; 390 } 391 392 static inline int error(splice_out_g & this, int error) { 393 int ret = close(this.fd); 394 if( ret != 0 ) { 395 mutex(serr) serr | "Failed to close fd" | errno; 396 } 397 return error(this.res, error); 398 } 399 400 static inline void wait_and_process(splice_out_g & this) { 401 wait(this.f); 402 403 // Did something crazy happen? 404 if(this.f.result > this.len) { 405 mutex(serr) serr | "SPLICE OUT spliced too much!"; 406 return error(this.res, -ERANGE); 407 } 408 409 // Something failed? 410 if(this.f.result < 0) { 411 int error = -this.f.result; 412 if( error == ECONNRESET ) return error(this, -ECONNRESET); 413 if( error == EPIPE ) return error(this, -EPIPE); 414 if( error == ECANCELED ) { 415 this.f.result = 0; 416 goto SHORT_WRITE; 417 } 418 if( error == EAGAIN || error == EWOULDBLOCK) { 419 mutex(serr) serr | "SPLICE OUT got eagain, WTF!"; 420 return error(this, -ECONNRESET); 421 } 422 } 423 424 // Done? 425 if(this.f.result == this.len) { 426 return done(this.res); 427 } 428 429 SHORT_WRITE: 430 // It must be a Short Write 431 this.len -= this.f.result; 432 reset(this.f); 433 return retry(this.res); 434 } 435 436 int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t fsize ) { 437 #if defined(LINKED_IO) 438 char buffer[512]; 439 int len = fill_header(buffer, fsize); 440 header_g header = { fd, buffer, len }; 441 splice_in_t splice_in = { ans_fd, pipe[1], fsize }; 442 splice_out_g splice_out = { pipe[0], fd, fsize }; 443 444 RETRY_LOOP: for() { 445 int have = need(header.res) + need(splice_in.res) + 1; 446 int idx = 0; 447 struct io_uring_sqe * sqes[3]; 448 __u32 idxs[3]; 449 struct $io_context * ctx = cfa_io_allocate(sqes, idxs, have); 450 451 if(need(splice_in.res)) { fill(splice_in, sqes[idx++]); } 452 if(need( header.res)) { fill(header , sqes[idx++]); } 453 fill(splice_out, sqes[idx]); 454 455 // Submit everything 456 asm volatile("": : :"memory"); 457 cfa_io_submit( ctx, idxs, have, false ); 458 459 // wait for the results 460 // Always wait for splice-in to complete as 461 // we may need to kill the connection if it fails 462 // If it already completed, this is a no-op 463 wait_and_process(splice_in); 464 465 if(is_error(splice_in.res)) { 466 mutex(serr) serr | "SPLICE IN failed with" | splice_in.res.error; 467 close(fd); 468 } 469 470 // Process the other 2 471 wait_and_process(header); 472 wait_and_process(splice_out); 473 474 if(is_done(splice_out.res)) { 475 break RETRY_LOOP; 476 } 477 478 // We need to wait for the completion if 479 // - both completed 480 // - the header failed 481 // - 482 483 if( is_error(header.res) 484 || is_error(splice_in.res) 485 || is_error(splice_out.res)) { 486 return -ECONNRESET; 487 } 488 } 489 490 return len + fsize; 491 #else 492 int ret = answer_header(fd, fsize); 493 if( ret < 0 ) { close(fd); return ret; } 494 return sendfile(pipe, fd, ans_fd, fsize); 495 #endif 496 } 497 498 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len) { 499 char * it = buffer; 500 size_t count = len - 1; 501 int rlen = 0; 502 READ: 503 for() { 504 int ret = cfa_recv(fd, (void*)it, count, 0, CFA_IO_LAZY); 505 // int ret = read(fd, (void*)it, count); 506 if(ret == 0 ) return [OK200, true, 0, 0]; 507 if(ret < 0 ) { 508 if( errno == EAGAIN || errno == EWOULDBLOCK) continue READ; 509 if( errno == ECONNRESET ) { close(fd); return [E408, true, 0, 0]; } 510 if( errno == EPIPE ) { close(fd); return [E408, true, 0, 0]; } 511 abort( "read error: (%d) %s\n", (int)errno, strerror(errno) ); 512 } 513 it[ret + 1] = '\0'; 514 rlen += ret; 515 516 if( strstr( it, "\r\n\r\n" ) ) break; 517 518 it += ret; 519 count -= ret; 520 521 if( count < 1 ) return [E414, false, 0, 0]; 522 } 523 524 if( options.log ) { 525 write(sout, buffer, rlen); 526 sout | nl; 527 } 528 529 it = buffer; 530 int ret = memcmp(it, "GET /", 5); 531 if( ret != 0 ) return [E400, false, 0, 0]; 532 it += 5; 533 534 char * end = strstr( it, " " ); 535 return [OK200, false, it, end - it]; 536 } 537 209 538 //============================================================================================= 210 539 … … 214 543 215 544 const char * original_http_msgs[] = { 216 "HTTP/1.1 200 OK\nServer: Htt oForall\nDate: %s \nContent-Type: text/plain\nContent-Length: ",217 "HTTP/1.1 200 OK\ nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 15\n\nHello, World!\n\n",218 "HTTP/1.1 400 Bad Request\nServer: Htt oForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",219 "HTTP/1.1 404 Not Found\nServer: Htt oForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",220 "HTTP/1.1 405 Method Not Allowed\nServer: Htt oForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",221 "HTTP/1.1 408 Request Timeout\nServer: Htt oForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",222 "HTTP/1.1 413 Payload Too Large\nServer: Htt oForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",223 "HTTP/1.1 414 URI Too Long\nServer: Htt oForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",545 "HTTP/1.1 200 OK\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: ", 546 "HTTP/1.1 200 OK\r\nServer: HttpForall\r\nDate\r\nConnection: keep-alive\r\nContent-Length: 15\r\nContent-Type: text/html: %s \r\n\r\nHello, World!\r\n", 547 "HTTP/1.1 400 Bad Request\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 548 "HTTP/1.1 404 Not Found\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 549 "HTTP/1.1 405 Method Not Allowed\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 550 "HTTP/1.1 408 Request Timeout\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 551 "HTTP/1.1 413 Payload Too Large\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 552 "HTTP/1.1 414 URI Too Long\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 224 553 }; 225 554 … … 251 580 Time now = timeHiRes(); 252 581 strftime( buff, 100, "%a, %d %b %Y %H:%M:%S %Z", now ); 253 sout | "Updated date to '" | buff | "'";582 // if( options.log ) sout | "Updated date to '" | buff | "'"; 254 583 255 584 for(i; KNOWN_CODES) { … … 264 593 this.idx = (this.idx + 1) % 2; 265 594 266 sout | "Date thread sleeping";595 // if( options.log ) sout | "Date thread sleeping"; 267 596 268 597 sleep(1`s); -
benchmark/io/http/protocol.hfa
rf95634e rb7fd9daf 16 16 17 17 int answer_error( int fd, HttpCode code ); 18 int answer_header( int fd, size_t size );19 18 int answer_plaintext( int fd ); 20 19 int answer_empty( int fd ); 20 int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t count ); 21 21 22 22 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len); 23 24 int sendfile( int pipe[2], int fd, int ans_fd, size_t count ); -
benchmark/io/http/worker.cfa
rf95634e rb7fd9daf 122 122 } 123 123 124 // Send the header125 int ret = answer_header(fd, count);126 if( ret == -ECONNRESET ) break REQUEST;127 128 124 // Send the desired file 129 ret =sendfile( this.pipe, fd, ans_fd, count);125 int ret = answer_sendfile( this.pipe, fd, ans_fd, count); 130 126 if( ret == -ECONNRESET ) break REQUEST; 131 127 … … 134 130 135 131 if( options.log ) sout | "=== Connection closed ==="; 136 close(fd);137 132 continue CONNECTION; 138 133 } -
benchmark/readyQ/cycle.cpp
rf95634e rb7fd9daf 41 41 Fibre * threads[tthreads]; 42 42 Partner thddata[tthreads]; 43 for( inti = 0; i < tthreads; i++) {43 for(unsigned i = 0; i < tthreads; i++) { 44 44 unsigned pi = (i + nthreads) % tthreads; 45 45 thddata[i].next = &thddata[pi].self; 46 46 } 47 for( inti = 0; i < tthreads; i++) {47 for(unsigned i = 0; i < tthreads; i++) { 48 48 threads[i] = new Fibre( reinterpret_cast<void (*)(void *)>(partner_main), &thddata[i] ); 49 49 } … … 53 53 start = timeHiRes(); 54 54 55 for( inti = 0; i < nthreads; i++) {55 for(unsigned i = 0; i < nthreads; i++) { 56 56 thddata[i].self.post(); 57 57 } … … 62 62 printf("\nDone\n"); 63 63 64 for( inti = 0; i < tthreads; i++) {64 for(unsigned i = 0; i < tthreads; i++) { 65 65 thddata[i].self.post(); 66 66 fibre_join( threads[i], nullptr ); -
benchmark/readyQ/cycle.go
rf95634e rb7fd9daf 60 60 atomic.StoreInt32(&stop, 1) 61 61 end := time.Now() 62 d elta:= end.Sub(start)62 duration := end.Sub(start) 63 63 64 64 fmt.Printf("\nDone\n") … … 74 74 75 75 p := message.NewPrinter(language.English) 76 p.Printf("Duration (ms) : % f\n", delta.Seconds());76 p.Printf("Duration (ms) : %d\n", duration.Milliseconds()) 77 77 p.Printf("Number of processors : %d\n", nprocs); 78 78 p.Printf("Number of threads : %d\n", tthreads); 79 79 p.Printf("Cycle size (# thrds) : %d\n", ring_size); 80 80 p.Printf("Total Operations(ops): %15d\n", global_counter) 81 p.Printf("Ops per second : %18.2f\n", float64(global_counter) / d elta.Seconds())82 p.Printf("ns per ops : %18.2f\n", float64(d elta.Nanoseconds()) / float64(global_counter))81 p.Printf("Ops per second : %18.2f\n", float64(global_counter) / duration.Seconds()) 82 p.Printf("ns per ops : %18.2f\n", float64(duration.Nanoseconds()) / float64(global_counter)) 83 83 p.Printf("Ops per threads : %15d\n", global_counter / uint64(tthreads)) 84 84 p.Printf("Ops per procs : %15d\n", global_counter / uint64(nprocs)) 85 p.Printf("Ops/sec/procs : %18.2f\n", (float64(global_counter) / float64(nprocs)) / d elta.Seconds())86 p.Printf("ns per ops/procs : %18.2f\n", float64(d elta.Nanoseconds()) / (float64(global_counter) / float64(nprocs)))85 p.Printf("Ops/sec/procs : %18.2f\n", (float64(global_counter) / float64(nprocs)) / duration.Seconds()) 86 p.Printf("ns per ops/procs : %18.2f\n", float64(duration.Nanoseconds()) / (float64(global_counter) / float64(nprocs))) 87 87 88 88 } -
benchmark/readyQ/cycle.rs
rf95634e rb7fd9daf 46 46 47 47 let tthreads = nthreads * ring_size; 48 let exp = Arc::new(bench::BenchData::new(options, tthreads ));48 let exp = Arc::new(bench::BenchData::new(options, tthreads, None)); 49 49 50 50 let s = (1000000 as u64).to_formatted_string(&Locale::en); -
benchmark/readyQ/locality.go
rf95634e rb7fd9daf 286 286 // Print with nice 's, i.e. 1'000'000 instead of 1000000 287 287 p := message.NewPrinter(language.English) 288 p.Printf("Duration ( s) : %f\n", delta.Seconds());288 p.Printf("Duration (ms) : %f\n", delta.Milliseconds()); 289 289 p.Printf("Number of processors : %d\n", nprocs); 290 290 p.Printf("Number of threads : %d\n", nthreads); -
benchmark/readyQ/locality.rs
rf95634e rb7fd9daf 124 124 return (r as *mut MyData, true); 125 125 } 126 let got = self.ptr.compare_ and_swap(expected, ctx as *mut MyCtx as u64, Ordering::SeqCst);127 if got == expected{126 let got = self.ptr.compare_exchange_weak(expected, ctx as *mut MyCtx as u64, Ordering::SeqCst, Ordering::SeqCst); 127 if got == Ok(expected) { 128 128 break expected;// We got the seat 129 129 } … … 285 285 assert_eq!(&s, "1,000,000"); 286 286 287 let exp = Arc::new(bench::BenchData::new(options, nprocs ));287 let exp = Arc::new(bench::BenchData::new(options, nprocs, None)); 288 288 let mut results = Result::new(); 289 289 -
benchmark/readyQ/transfer.cfa
rf95634e rb7fd9daf 39 39 Pause(); 40 40 if( (timeHiRes() - start) > 5`s ) { 41 print_stats_now( bench_cluster, CFA_STATS_READY_Q | CFA_STATS_IO ); 41 42 serr | "Programs has been blocked for more than 5 secs"; 42 43 exit(1); … … 110 111 cfa_option opt[] = { 111 112 BENCH_OPT, 112 { 'e', "exhaust", "Whether or not threads that have seen the new epoch should yield or park.", exhaust, parse_yesno}113 { 'e', "exhaust", "Whether or not threads that have seen the new epoch should park instead of yielding.", exhaust, parse_yesno} 113 114 }; 114 115 BENCH_OPT_PARSE("cforall transition benchmark"); … … 166 167 } 167 168 168 sout | "Duration : " | ws(3, 3, unit(eng((end - start)`ds))) | 's';169 sout | "Duration (ms) : " | ws(3, 3, unit(eng((end - start)`dms))); 169 170 sout | "Number of processors : " | nprocs; 170 171 sout | "Number of threads : " | nthreads; -
benchmark/readyQ/transfer.cpp
rf95634e rb7fd9daf 173 173 } 174 174 175 std::cout << "Duration : " << to_miliseconds(end - start) << "ms"<< std::endl;175 std::cout << "Duration (ms) : " << to_miliseconds(end - start) << std::endl; 176 176 std::cout << "Number of processors : " << nprocs << std::endl; 177 177 std::cout << "Number of threads : " << nthreads << std::endl; -
benchmark/readyQ/yield.cfa
rf95634e rb7fd9daf 80 80 } 81 81 82 printf("Took %'ld ms\n", (end - start)`ms); 82 printf("Duration (ms) : %'ld\n", (end - start)`dms); 83 printf("Number of processors: %'d\n", nprocs); 84 printf("Number of threads : %'d\n", nthreads); 85 printf("Total yields : %'15llu\n", global_counter); 83 86 printf("Yields per second : %'18.2lf\n", ((double)global_counter) / (end - start)`s); 84 87 printf("ns per yields : %'18.2lf\n", ((double)(end - start)`ns) / global_counter); 85 printf("Total yields : %'15llu\n", global_counter);86 88 printf("Yields per procs : %'15llu\n", global_counter / nprocs); 87 89 printf("Yields/sec/procs : %'18.2lf\n", (((double)global_counter) / nprocs) / (end - start)`s); -
benchmark/readyQ/yield.cpp
rf95634e rb7fd9daf 154 154 155 155 auto dur_nano = duration_cast<std::nano>(duration); 156 auto dur_dms = duration_cast<std::milli>(duration); 156 157 157 std::cout << "Took " << duration << " s\n";158 printf("Duration (ms) : %'.2lf\n", dur_dms ); 158 159 printf("Total yields : %'15llu\n", global_counter ); 159 160 printf("Yields per procs : %'15llu\n", global_counter / nprocs ); -
benchmark/rmit.py
rf95634e rb7fd9daf 16 16 import random 17 17 import re 18 import socket 18 19 import subprocess 19 20 import sys … … 95 96 return nopts 96 97 98 # returns the first option with key 'opt' 99 def search_option(action, opt): 100 i = 0 101 while i < len(action): 102 if action[i] == opt: 103 i += 1 104 if i != len(action): 105 return action[i] 106 i += 1 107 108 return None 109 97 110 def actions_eta(actions): 98 111 time = 0 99 112 for a in actions: 100 i = 0 101 while i < len(a): 102 if a[i] == '-d': 103 i += 1 104 if i != len(a): 105 time += int(a[i]) 106 i += 1 113 o = search_option(a, '-d') 114 if o : 115 time += int(o) 107 116 return time 117 118 taskset_maps = None 119 120 def init_taskset_maps(): 121 global taskset_maps 122 known_hosts = { 123 "jax": { 124 range( 1, 24) : "48-71", 125 range( 25, 48) : "48-71,144-167", 126 range( 49, 96) : "48-95,144-191", 127 range( 97, 144) : "24-95,120-191", 128 range(145, 192) : "0-95,96-191", 129 }, 130 } 131 132 if (host := socket.gethostname()) in known_hosts: 133 taskset_maps = known_hosts[host] 134 return True 135 136 print("Warning unknown host '{}', disable taskset usage".format(host), file=sys.stderr) 137 return False 138 139 140 def settaskset_one(action): 141 o = search_option(action, '-p') 142 if not o: 143 return action 144 try: 145 oi = int(o) 146 except ValueError: 147 return action 148 149 m = "Not found" 150 for key in taskset_maps: 151 if oi in key: 152 return ['taskset', '-c', taskset_maps[key], *action] 153 154 print("Warning no mapping for {} cores".format(oi), file=sys.stderr) 155 return action 156 157 def settaskset(actions): 158 return [settaskset_one(a) for a in actions] 108 159 109 160 if __name__ == "__main__": … … 115 166 parser.add_argument('--file', nargs='?', type=argparse.FileType('w'), default=sys.stdout) 116 167 parser.add_argument('--trials', help='Number of trials to run per combinaison', type=int, default=3) 168 parser.add_argument('--notaskset', help='If specified, the trial will not use taskset to match the -p option', action='store_true') 117 169 parser.add_argument('command', metavar='command', type=str, nargs=1, help='the command prefix to run') 118 170 parser.add_argument('candidates', metavar='candidates', type=str, nargs='*', help='the candidate suffix to run') … … 170 222 171 223 # ================================================================================ 172 # Figure out all the combinations to run 224 # Fixup the different commands 225 226 # Add tasksets 227 withtaskset = False 228 if not options.notaskset and init_taskset_maps(): 229 withtaskset = True 230 actions = settaskset(actions) 231 232 # ================================================================================ 233 # Now that we know what to run, print it. 234 # find expected time 235 time = actions_eta(actions) 236 print("Running {} trials{}".format(len(actions), "" if time == 0 else " (expecting to take {})".format(str(datetime.timedelta(seconds=int(time)))) )) 237 238 # dry run if options ask for it 173 239 if options.list: 174 240 for a in actions: … … 180 246 # Prepare to run 181 247 182 # find expected time183 time = actions_eta(actions)184 print("Running {} trials{}".format(len(actions), "" if time == 0 else " (expecting to take {})".format(str(datetime.timedelta(seconds=int(time)))) ))185 186 248 random.shuffle(actions) 187 249 … … 191 253 first = True 192 254 for i, a in enumerate(actions): 193 sa = " ".join(a )255 sa = " ".join(a[3:] if withtaskset else a) 194 256 if first: 195 257 first = False … … 208 270 match = re.search("^(.*):(.*)$", s) 209 271 if match: 210 fields[match.group(1).strip()] = float(match.group(2).strip().replace(',','')) 211 212 options.file.write(json.dumps([a[0][2:], sa, fields])) 272 try: 273 fields[match.group(1).strip()] = float(match.group(2).strip().replace(',','')) 274 except: 275 pass 276 277 options.file.write(json.dumps([a[3 if withtaskset else 0][2:], sa, fields])) 213 278 options.file.flush() 214 279 -
doc/theses/andrew_beach_MMath/Makefile
rf95634e rb7fd9daf 31 31 32 32 # The main rule, it does all the tex/latex processing. 33 ${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} Makefile | ${BUILD}33 ${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} termhandle.pstex resumhandle.pstex Makefile | ${BUILD} 34 34 ${LATEX} ${BASE} 35 35 ${BIBTEX} ${BUILD}/${BASE} … … 40 40 ${FIGTEX}: ${BUILD}/%.tex: %.fig | ${BUILD} 41 41 fig2dev -L eepic $< > $@ 42 43 %.pstex : %.fig | ${Build} 44 fig2dev -L pstex $< > ${BUILD}/$@ 45 fig2dev -L pstex_t -p ${BUILD}/$@ $< > ${BUILD}/$@_t 42 46 43 47 # Step through dvi & postscript to handle xfig specials. -
doc/theses/andrew_beach_MMath/code/CondCatch.java
rf95634e rb7fd9daf 6 6 static boolean should_catch = false; 7 7 8 static void throw_exception() throws EmptyException {9 throw new EmptyException();10 }11 12 static void cond_catch() throws EmptyException {13 try {14 throw_exception();15 } catch (EmptyException exc) {16 if (!should_catch) {17 throw exc;18 }19 }20 }21 22 8 private static long loop(int times) { 23 9 long startTime = System.nanoTime(); 24 10 for (int count = 0 ; count < times ; ++count) { 25 11 try { 26 cond_catch(); 12 try { 13 throw new EmptyException(); 14 } catch (EmptyException exc) { 15 if (!should_catch) { 16 throw exc; 17 } 18 } 27 19 } catch (EmptyException exc) { 28 20 // ... … … 46 38 47 39 long time = loop(times); 48 System.out. println("Run-Time (ns): " + time);40 System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.); 49 41 } 50 42 } -
doc/theses/andrew_beach_MMath/code/ThrowEmpty.java
rf95634e rb7fd9daf 39 39 40 40 long time = loop(times, total_frames); 41 System.out. println("Run-Time (ns): " + time);41 System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.); 42 42 } 43 43 } -
doc/theses/andrew_beach_MMath/code/ThrowFinally.java
rf95634e rb7fd9daf 44 44 45 45 long time = loop(times, total_frames); 46 System.out. println("Run-Time (ns): " + time);46 System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.); 47 47 } 48 48 } -
doc/theses/andrew_beach_MMath/code/ThrowOther.java
rf95634e rb7fd9daf 52 52 53 53 long time = loop(times, total_frames); 54 System.out. println("Run-Time (ns): " + time);54 System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.); 55 55 } 56 56 } -
doc/theses/andrew_beach_MMath/code/cond-catch.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.h >5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 10 9 11 10 bool should_catch = false; 12 13 void throw_exception() {14 throw (empty_exception){&empty_vt};15 }16 17 void cond_catch() {18 try {19 throw_exception();20 } catch (empty_exception * exc ; should_catch) {21 asm volatile ("# catch block (conditional)");22 }23 }24 11 25 12 int main(int argc, char * argv[]) { 26 13 unsigned int times = 1; 27 14 if (1 < argc) { 28 times = strto l(argv[1], 0p, 10);15 times = strto(argv[1], 0p, 10); 29 16 } 30 17 if (2 < argc) { 31 should_catch = strtol(argv[2], 0p, 10);18 should_catch = (unsigned int)strto(argv[2], 0p, 2); 32 19 } 33 20 … … 35 22 for (unsigned int count = 0 ; count < times ; ++count) { 36 23 try { 37 cond_catch(); 24 throw (empty_exception){&empty_vt}; 25 } catch (empty_exception * exc ; should_catch) { 26 asm volatile ("# catch block (conditional)"); 38 27 } catch (empty_exception * exc) { 39 28 asm volatile ("# catch block (unconditional)"); … … 41 30 } 42 31 Time end_time = timeHiRes(); 43 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;32 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 44 33 } -
doc/theses/andrew_beach_MMath/code/cond-catch.cpp
rf95634e rb7fd9daf 4 4 #include <exception> 5 5 #include <iostream> 6 #include <iomanip> 6 7 8 using namespace std; 7 9 using namespace std::chrono; 8 10 … … 10 12 11 13 bool should_catch = false; 12 13 void throw_exception() {14 throw EmptyException();15 }16 17 void cond_catch() {18 try {19 throw_exception();20 } catch (EmptyException & exc) {21 if (!should_catch) {22 throw;23 }24 asm volatile ("# catch block (conditional)");25 }26 }27 14 28 15 int main(int argc, char * argv[]) { … … 38 25 for (unsigned int count = 0 ; count < times ; ++count) { 39 26 try { 40 cond_catch(); 27 try { 28 throw EmptyException(); 29 } catch (EmptyException & exc) { 30 if (!should_catch) { 31 throw; 32 } 33 asm volatile ("# catch block (conditional)"); 34 } 41 35 } catch (EmptyException &) { 42 36 asm volatile ("# catch block (unconditional)"); … … 45 39 time_point<steady_clock> end_time = steady_clock::now(); 46 40 nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time); 47 std::cout << "Run-Time (ns): " << duration.count() << std::endl;41 cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl; 48 42 } -
doc/theses/andrew_beach_MMath/code/cond-fixup.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 10 9 11 10 bool should_catch = false; 12 13 void throw_exception() {14 throwResume (empty_exception){&empty_vt};15 }16 17 void cond_catch() {18 try {19 throw_exception();20 } catchResume (empty_exception * exc ; should_catch) {21 asm volatile ("# fixup block (conditional)");22 }23 }24 11 25 12 int main(int argc, char * argv[]) { 26 13 unsigned int times = 1; 27 14 if (1 < argc) { 28 times = strto l(argv[1], 0p, 10);15 times = strto(argv[1], 0p, 10); 29 16 } 30 17 if (2 < argc) { 31 should_catch = strtol(argv[2], 0p, 10);18 should_catch = (unsigned int)strto(argv[2], 0p, 2); 32 19 } 33 20 … … 35 22 for (unsigned int count = 0 ; count < times ; ++count) { 36 23 try { 37 cond_catch(); 24 throwResume (empty_exception){&empty_vt}; 25 } catchResume (empty_exception * exc ; should_catch) { 26 asm volatile ("# fixup block (conditional)"); 38 27 } catchResume (empty_exception * exc) { 39 28 asm volatile ("# fixup block (unconditional)"); … … 41 30 } 42 31 Time end_time = timeHiRes(); 43 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;32 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 44 33 } -
doc/theses/andrew_beach_MMath/code/resume-detor.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 10 9 11 10 struct WithDestructor {}; … … 17 16 void unwind_destructor(unsigned int frames) { 18 17 if (frames) { 19 20 18 WithDestructor object; 21 19 unwind_destructor(frames - 1); … … 29 27 unsigned int total_frames = 1; 30 28 if (1 < argc) { 31 times = strto l(argv[1], 0p, 10);29 times = strto(argv[1], 0p, 10); 32 30 } 33 31 if (2 < argc) { 34 total_frames = strto l(argv[2], 0p, 10);32 total_frames = strto(argv[2], 0p, 10); 35 33 } 36 34 … … 44 42 } 45 43 Time end_time = timeHiRes(); 46 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;44 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 47 45 } -
doc/theses/andrew_beach_MMath/code/resume-empty.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 8 9 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 10 11 void unwind_empty(unsigned int frames) { 10 void nounwind_empty(unsigned int frames) { 12 11 if (frames) { 13 unwind_empty(frames - 1); 12 nounwind_empty(frames - 1); 13 if ( frames == -1 ) printf( "42" ); // prevent recursion optimizations 14 14 } else { 15 15 throwResume (empty_exception){&empty_vt}; … … 21 21 unsigned int total_frames = 1; 22 22 if (1 < argc) { 23 times = strto l(argv[1], 0p, 10);23 times = strto(argv[1], 0p, 10); 24 24 } 25 25 if (2 < argc) { 26 total_frames = strto l(argv[2], 0p, 10);26 total_frames = strto(argv[2], 0p, 10); 27 27 } 28 28 29 29 Time start_time = timeHiRes(); 30 for ( int count = 0 ; count < times ; ++count) {30 for (unsigned int count = 0 ; count < times ; ++count) { 31 31 try { 32 unwind_empty(total_frames);32 nounwind_empty(total_frames); 33 33 } catchResume (empty_exception *) { 34 34 asm volatile ("# fixup block"); … … 36 36 } 37 37 Time end_time = timeHiRes(); 38 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;38 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 39 39 } -
doc/theses/andrew_beach_MMath/code/resume-finally.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 10 9 11 10 void unwind_finally(unsigned int frames) { … … 25 24 unsigned int total_frames = 1; 26 25 if (1 < argc) { 27 times = strto l(argv[1], 0p, 10);26 times = strto(argv[1], 0p, 10); 28 27 } 29 28 if (2 < argc) { 30 total_frames = strto l(argv[2], 0p, 10);29 total_frames = strto(argv[2], 0p, 10); 31 30 } 32 31 … … 40 39 } 41 40 Time end_time = timeHiRes(); 42 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;41 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 43 42 } -
doc/theses/andrew_beach_MMath/code/resume-other.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 9 exception not_raised_exception; 8 10 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 10 11 EHM_EXCEPTION(not_raised_exception)(); 12 13 void unwind_other(unsigned int frames) { 11 void nounwind_other(unsigned int frames) { 14 12 if (frames) { 15 13 try { 16 unwind_other(frames - 1);14 nounwind_other(frames - 1); 17 15 } catchResume (not_raised_exception *) { 18 16 asm volatile ("# fixup block (stack)"); … … 27 25 unsigned int total_frames = 1; 28 26 if (1 < argc) { 29 times = strto l(argv[1], 0p, 10);27 times = strto(argv[1], 0p, 10); 30 28 } 31 29 if (2 < argc) { 32 total_frames = strto l(argv[2], 0p, 10);30 total_frames = strto(argv[2], 0p, 10); 33 31 } 34 32 … … 36 34 for (int count = 0 ; count < times ; ++count) { 37 35 try { 38 unwind_other(total_frames);36 nounwind_other(total_frames); 39 37 } catchResume (empty_exception *) { 40 38 asm volatile ("# fixup block (base)"); … … 42 40 } 43 41 Time end_time = timeHiRes(); 44 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;42 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 45 43 } -
doc/theses/andrew_beach_MMath/code/run.sh
rf95634e rb7fd9daf 1 1 #!/usr/bin/env bash 2 2 3 readonly ALL_TESTS=( cond-match-{all,none} cross-{catch,finally} \4 raise-{detor,empty,finally,other})3 readonly ALL_TESTS=(raise-{empty,detor,finally,other} try-{catch,finally} \ 4 cond-match-{all,none} fixup-{empty,other}) 5 5 6 6 gen-file-name() ( … … 18 18 ) 19 19 20 readonly N=${1:- 5}20 readonly N=${1:-1} 21 21 readonly OUT_FILE=$(gen-file-name ${2:-run-%-$N}) 22 22 -
doc/theses/andrew_beach_MMath/code/test.sh
rf95634e rb7fd9daf 4 4 # test.sh LANGUAGE TEST 5 5 # Run the TEST in LANGUAGE. 6 # test.sh -a 7 # Build all tests. 6 8 # test.sh -b SOURCE_FILE... 7 9 # Build a test from SOURCE_FILE(s). 10 # test.sh -c 11 # Clean all executables. 8 12 # test.sh -v LANGUAGE TEST FILE 9 13 # View the result from TEST in LANGUAGE stored in FILE. 10 14 11 readonly ITERATIONS=1000000 # 1 000 000, one million 15 readonly DIR=$(dirname "$(readlink -f "$0")") 16 cd $DIR 17 18 readonly MIL=000000 19 # Various preset values used as arguments. 20 readonly ITERS_1M=1$MIL 21 readonly ITERS_10M=10$MIL 22 readonly ITERS_100M=100$MIL 23 readonly ITERS_1000M=1000$MIL 12 24 readonly STACK_HEIGHT=100 13 25 … … 23 35 case "$1" in 24 36 *.cfa) 25 # Requires a symbolic link. 26 mmake "${1%.cfa}" "$1" ./cfa -DNDEBUG -nodebug -O3 "$1" -o "${1%.cfa}" 37 # A symbolic link/local copy can be used as an override. 38 cmd=./cfa 39 if [ ! -x $cmd ]; then 40 cmd=cfa 41 fi 42 mmake "${1%.cfa}" "$1" $cmd -DNDEBUG -nodebug -O3 "$1" -o "${1%.cfa}" 27 43 ;; 28 44 *.cpp) 29 mmake "${1%.cpp}-cpp" "$1" g++ -DNDEBUG -O3 "$1" -o "${1%.cpp}-cpp"45 mmake "${1%.cpp}-cpp" "$1" g++-10 -DNDEBUG -O3 "$1" -o "${1%.cpp}-cpp" 30 46 ;; 31 47 *.java) … … 39 55 ) 40 56 41 if [ "-b" = "$1" ]; then 57 if [ "-a" = "$1" ]; then 58 for file in *.cfa *.cpp *.java; do 59 build $file 60 done 61 exit 0 62 elif [ "-b" = "$1" ]; then 42 63 for file in "${@:2}"; do 43 64 build $file 44 65 done 45 66 exit 0 67 elif [ "-c" = "$1" ]; then 68 rm $(basename -s ".cfa" -a *.cfa) 69 rm $(basename -s ".cpp" -a *.cpp) 70 rm *-cpp 71 rm *.class 72 exit 0 46 73 elif [ "-v" = "$1" -a 4 = "$#" ]; then 47 TEST_LANG="$2"48 TEST_CASE="$3"49 VIEW_FILE="$4"74 TEST_LANG="$2" 75 TEST_CASE="$3" 76 VIEW_FILE="$4" 50 77 elif [ 2 -eq "$#" ]; then 51 78 TEST_LANG="$1" … … 63 90 64 91 case "$TEST_CASE" in 65 cond-match-all) 66 CFAT="./cond-catch $ITERATIONS 1" 67 CFAR="./cond-fixup $ITERATIONS 1" 68 CPP="./cond-catch-cpp $ITERATIONS 1" 69 JAVA="java CondCatch $ITERATIONS 1" 70 PYTHON="./cond_catch.py $ITERATIONS 1" 71 ;; 72 cond-match-none) 73 CFAT="./cond-catch $ITERATIONS 0" 74 CFAR="./cond-fixup $ITERATIONS 0" 75 CPP="./cond-catch-cpp $ITERATIONS 0" 76 JAVA="java CondCatch $ITERATIONS 0" 77 PYTHON="./cond_catch.py $ITERATIONS 0" 78 ;; 79 cross-catch) 80 CFAT="./cross-catch $ITERATIONS" 81 CFAR="./cross-resume $ITERATIONS" 82 CPP="./cross-catch-cpp $ITERATIONS" 83 JAVA="java CrossCatch $ITERATIONS" 84 PYTHON="./cross_catch.py $ITERATIONS" 85 ;; 86 cross-finally) 87 CFAT="./cross-finally $ITERATIONS" 88 CFAR=unsupported 89 CPP=unsupported 90 JAVA="java CrossFinally $ITERATIONS" 91 PYTHON="./cross_finally.py $ITERATIONS" 92 raise-empty) 93 CFAT="./throw-empty $ITERS_1M $STACK_HEIGHT" 94 CFAR="./resume-empty $ITERS_10M $STACK_HEIGHT" 95 CPP="./throw-empty-cpp $ITERS_1M $STACK_HEIGHT" 96 JAVA="java ThrowEmpty $ITERS_1M $STACK_HEIGHT" 97 PYTHON="./throw-empty.py $ITERS_1M $STACK_HEIGHT" 92 98 ;; 93 99 raise-detor) 94 CFAT="./throw-detor $ITER ATIONS$STACK_HEIGHT"95 CFAR="./resume-detor $ITER ATIONS$STACK_HEIGHT"96 CPP="./throw-detor-cpp $ITER ATIONS$STACK_HEIGHT"100 CFAT="./throw-detor $ITERS_1M $STACK_HEIGHT" 101 CFAR="./resume-detor $ITERS_10M $STACK_HEIGHT" 102 CPP="./throw-detor-cpp $ITERS_1M $STACK_HEIGHT" 97 103 JAVA=unsupported 98 104 PYTHON=unsupported 99 105 ;; 100 raise-empty)101 CFAT="./throw-empty $ITERATIONS $STACK_HEIGHT"102 CFAR="./resume-empty $ITERATIONS $STACK_HEIGHT"103 CPP="./throw-empty-cpp $ITERATIONS $STACK_HEIGHT"104 JAVA="java ThrowEmpty $ITERATIONS $STACK_HEIGHT"105 PYTHON="./throw_empty.py $ITERATIONS $STACK_HEIGHT"106 ;;107 106 raise-finally) 108 CFAT="./throw-finally $ITER ATIONS$STACK_HEIGHT"109 CFAR="./resume-finally $ITER ATIONS$STACK_HEIGHT"107 CFAT="./throw-finally $ITERS_1M $STACK_HEIGHT" 108 CFAR="./resume-finally $ITERS_10M $STACK_HEIGHT" 110 109 CPP=unsupported 111 JAVA="java ThrowFinally $ITER ATIONS$STACK_HEIGHT"112 PYTHON="./throw _finally.py $ITERATIONS$STACK_HEIGHT"110 JAVA="java ThrowFinally $ITERS_1M $STACK_HEIGHT" 111 PYTHON="./throw-finally.py $ITERS_1M $STACK_HEIGHT" 113 112 ;; 114 113 raise-other) 115 CFAT="./throw-other $ITERATIONS $STACK_HEIGHT" 116 CFAR="./resume-other $ITERATIONS $STACK_HEIGHT" 117 CPP="./throw-other-cpp $ITERATIONS $STACK_HEIGHT" 118 JAVA="java ThrowOther $ITERATIONS $STACK_HEIGHT" 119 PYTHON="./throw_other.py $ITERATIONS $STACK_HEIGHT" 114 CFAT="./throw-other $ITERS_1M $STACK_HEIGHT" 115 CFAR="./resume-other $ITERS_10M $STACK_HEIGHT" 116 CPP="./throw-other-cpp $ITERS_1M $STACK_HEIGHT" 117 JAVA="java ThrowOther $ITERS_1M $STACK_HEIGHT" 118 PYTHON="./throw-other.py $ITERS_1M $STACK_HEIGHT" 119 ;; 120 try-catch) 121 CFAT="./try-catch $ITERS_1000M" 122 CFAR="./try-resume $ITERS_1000M" 123 CPP="./try-catch-cpp $ITERS_1000M" 124 JAVA="java TryCatch $ITERS_1000M" 125 PYTHON="./try-catch.py $ITERS_1000M" 126 ;; 127 try-finally) 128 CFAT="./try-finally $ITERS_1000M" 129 CFAR=unsupported 130 CPP=unsupported 131 JAVA="java TryFinally $ITERS_1000M" 132 PYTHON="./try-finally.py $ITERS_1000M" 133 ;; 134 cond-match-all) 135 CFAT="./cond-catch $ITERS_10M 1" 136 CFAR="./cond-fixup $ITERS_100M 1" 137 CPP="./cond-catch-cpp $ITERS_10M 1" 138 JAVA="java CondCatch $ITERS_10M 1" 139 PYTHON="./cond-catch.py $ITERS_10M 1" 140 ;; 141 cond-match-none) 142 CFAT="./cond-catch $ITERS_10M 0" 143 CFAR="./cond-fixup $ITERS_100M 0" 144 CPP="./cond-catch-cpp $ITERS_10M 0" 145 JAVA="java CondCatch $ITERS_10M 0" 146 PYTHON="./cond-catch.py $ITERS_10M 0" 147 ;; 148 fixup-empty) 149 CFAT="./fixup-empty-f $ITERS_10M $STACK_HEIGHT" 150 CFAR="./fixup-empty-r $ITERS_10M $STACK_HEIGHT" 151 CPP="./fixup-empty-cpp $ITERS_10M $STACK_HEIGHT" 152 JAVA="java FixupEmpty $ITERS_10M $STACK_HEIGHT" 153 PYTHON="./fixup-empty.py $ITERS_10M $STACK_HEIGHT" 154 ;; 155 fixup-other) 156 CFAT="./fixup-other-f $ITERS_10M $STACK_HEIGHT" 157 CFAR="./fixup-other-r $ITERS_10M $STACK_HEIGHT" 158 CPP="./fixup-other-cpp $ITERS_10M $STACK_HEIGHT" 159 JAVA="java FixupOther $ITERS_10M $STACK_HEIGHT" 160 PYTHON="./fixup-other.py $ITERS_10M $STACK_HEIGHT" 120 161 ;; 121 162 *) … … 140 181 141 182 if [ -n "$VIEW_FILE" ]; then 142 grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time (ns): !!;T;p'143 exit183 grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time.*: !!;T;p' 184 exit 144 185 fi 145 186 -
doc/theses/andrew_beach_MMath/code/throw-detor.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 10 9 11 10 struct WithDestructor {}; … … 28 27 unsigned int total_frames = 1; 29 28 if (1 < argc) { 30 times = strto l(argv[1], 0p, 10);29 times = strto(argv[1], 0p, 10); 31 30 } 32 31 if (2 < argc) { 33 total_frames = strto l(argv[2], 0p, 10);32 total_frames = strto(argv[2], 0p, 10); 34 33 } 35 34 … … 43 42 } 44 43 Time end_time = timeHiRes(); 45 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;44 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 46 45 } -
doc/theses/andrew_beach_MMath/code/throw-detor.cpp
rf95634e rb7fd9daf 4 4 #include <exception> 5 5 #include <iostream> 6 #include <iomanip> 6 7 8 using namespace std; 7 9 using namespace std::chrono; 8 10 … … 44 46 time_point<steady_clock> end_time = steady_clock::now(); 45 47 nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time); 46 std::cout << "Run-Time (ns): " << duration.count() << std::endl;48 cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl; 47 49 } -
doc/theses/andrew_beach_MMath/code/throw-empty.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 10 9 11 10 void unwind_empty(unsigned int frames) { 12 11 if (frames) { 13 12 unwind_empty(frames - 1); 13 if ( frames == -1 ) printf( "42" ); // prevent recursion optimizations 14 14 } else { 15 15 throw (empty_exception){&empty_vt}; … … 21 21 unsigned int total_frames = 1; 22 22 if (1 < argc) { 23 times = strto l(argv[1], 0p, 10);23 times = strto(argv[1], 0p, 10); 24 24 } 25 25 if (2 < argc) { 26 total_frames = strto l(argv[2], 0p, 10);26 total_frames = strto(argv[2], 0p, 10); 27 27 } 28 28 … … 36 36 } 37 37 Time end_time = timeHiRes(); 38 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;38 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 39 39 } -
doc/theses/andrew_beach_MMath/code/throw-empty.cpp
rf95634e rb7fd9daf 1 1 // Throw Across Empty Function 2 2 #include <chrono> 3 #include <cstdio> 3 4 #include <cstdlib> 4 5 #include <exception> 5 6 #include <iostream> 7 #include <iomanip> 6 8 9 using namespace std; 7 10 using namespace std::chrono; 8 11 … … 12 15 if (frames) { 13 16 unwind_empty(frames - 1); 17 if (-1 == frames) printf("~"); 14 18 } else { 15 19 throw (EmptyException){}; … … 37 41 time_point<steady_clock> end_time = steady_clock::now(); 38 42 nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time); 39 std::cout << "Run-Time (ns): " << duration.count() << std::endl;43 cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl; 40 44 } -
doc/theses/andrew_beach_MMath/code/throw-finally.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 8 9 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 10 unsigned int frames; // use global because of gcc thunk problem 10 11 11 void unwind_finally(unsigned int frames) {12 void unwind_finally(unsigned int dummy) { 12 13 if (frames) { 14 frames -= 1; 13 15 try { 14 unwind_finally( frames - 1);16 unwind_finally(42); 15 17 } finally { 16 18 asm volatile ("# finally block"); 17 19 } 18 20 } else { 21 dummy = 42; 19 22 throw (empty_exception){&empty_vt}; 20 23 } … … 25 28 unsigned int total_frames = 1; 26 29 if (1 < argc) { 27 times = strto l(argv[1], 0p, 10);30 times = strto(argv[1], 0p, 10); 28 31 } 29 32 if (2 < argc) { 30 total_frames = strto l(argv[2], 0p, 10);33 total_frames = strto(argv[2], 0p, 10); 31 34 } 35 frames = total_frames; 32 36 33 37 Time start_time = timeHiRes(); 34 38 for (int count = 0 ; count < times ; ++count) { 35 39 try { 36 unwind_finally( total_frames);40 unwind_finally(42); 37 41 } catch (empty_exception *) { 38 42 asm volatile ("# catch block"); … … 40 44 } 41 45 Time end_time = timeHiRes(); 42 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;46 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 43 47 } -
doc/theses/andrew_beach_MMath/code/throw-other.cfa
rf95634e rb7fd9daf 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> 5 #include <stdlib.hfa> // strto 6 6 7 EHM_EXCEPTION(empty_exception)(); 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 9 exception not_raised_exception; 8 10 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 11 unsigned int frames; // use global because of gcc thunk problem 10 12 11 EHM_EXCEPTION(not_raised_exception)(); 12 13 void unwind_other(unsigned int frames) { 13 void unwind_other(unsigned int dummy) { 14 14 if (frames) { 15 frames -= 1; 15 16 try { 16 unwind_other( frames - 1);17 unwind_other(42); 17 18 } catch (not_raised_exception *) { 18 19 asm volatile ("# catch block (stack)"); 19 20 } 20 21 } else { 22 dummy = 42; 21 23 throw (empty_exception){&empty_vt}; 22 24 } … … 27 29 unsigned int total_frames = 1; 28 30 if (1 < argc) { 29 times = strto l(argv[1], 0p, 10);31 times = strto(argv[1], 0p, 10); 30 32 } 31 33 if (2 < argc) { 32 total_frames = strto l(argv[2], 0p, 10);34 total_frames = strto(argv[2], 0p, 10); 33 35 } 36 frames = total_frames; 34 37 35 38 Time start_time = timeHiRes(); 36 39 for (int count = 0 ; count < times ; ++count) { 37 40 try { 38 unwind_other( total_frames);41 unwind_other(42); 39 42 } catch (empty_exception *) { 40 43 asm volatile ("# catch block (base)"); … … 42 45 } 43 46 Time end_time = timeHiRes(); 44 sout | "Run-Time ( ns): " | (end_time - start_time)`ns;47 sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.); 45 48 } -
doc/theses/andrew_beach_MMath/code/throw-other.cpp
rf95634e rb7fd9daf 4 4 #include <exception> 5 5 #include <iostream> 6 #include <iomanip> 6 7 8 using namespace std; 7 9 using namespace std::chrono; 8 10 … … 43 45 time_point<steady_clock> end_time = steady_clock::now(); 44 46 nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time); 45 std::cout << "Run-Time (ns): " << duration.count() << std::endl;47 cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl; 46 48 } -
doc/theses/andrew_beach_MMath/conclusion.tex
rf95634e rb7fd9daf 1 1 \chapter{Conclusion} 2 \label{c:conclusion} 2 3 % Just a little knot to tie the paper together. 3 4 4 In the previous chapters this thesis presents the design and implementation5 In the previous chapters, this thesis presents the design and implementation 5 6 of \CFA's exception handling mechanism (EHM). 6 Both the design and implementation are based off of tools and techniques 7 developed for other programming languages but they were adapted to better fit 8 \CFA's feature set. 7 Both the design and implementation are based off of tools and 8 techniques developed for other programming languages but they were adapted to 9 better fit \CFA's feature set and add a few features that do not exist in 10 other EHMs, 11 including conditional matching, default handlers for unhandled exceptions 12 and cancellation though coroutines and threads back to the program main stack. 9 13 10 14 The resulting features cover all of the major use cases of the most popular 11 15 termination EHMs of today, along with reintroducing resumption exceptions and 12 creating some new features that fix with \CFA's larger programming patterns. 16 creating some new features that fit with \CFA's larger programming patterns, 17 such as virtuals independent of traditional objects. 13 18 14 The implementation has been tested and compared to other implementations. 19 The \CFA project's test suite has been expanded to test the EHM. 20 The implementation's performance has also been 21 compared to other implementations with a small set of targeted 22 micro-benchmarks. 15 23 The results, while not cutting edge, are good enough for prototyping, which 16 is \CFA's stage of development.24 is \CFA's current stage of development. 17 25 18 This is a valuable new feature for \CFA in its own right but also serves 19 as a tool (and motivation) for other developments in the language. 26 This initial EHM will bring valuable new features to \CFA in its own right 27 but also serves as a tool and motivation for other developments in the 28 language. -
doc/theses/andrew_beach_MMath/exception-layout.fig
rf95634e rb7fd9daf 28 28 0 0 1.00 240.00 240.00 29 29 360 405 360 2070 30 4 0 0 50 -1 0 12 0.0000 4135 1080 2700 585 Fixed Header\00131 4 0 0 50 -1 0 12 0.0000 4 135 1710540 990 Cforall Information\00132 4 0 0 50 -1 0 12 0.0000 4 165 1530540 585 _Unwind_Exception\00133 4 0 0 50 -1 0 12 0.0000 4 165 1260540 1530 User Exception\00134 4 0 0 50 -1 0 12 0.0000 4 165 11702655 1530 Variable Body\00135 4 0 0 50 -1 0 12 0.0000 4 165 1260 2655 1215 (Fixed Offset)\00130 4 0 0 50 -1 0 12 0.0000 0 135 1080 2700 585 Fixed Header\001 31 4 0 0 50 -1 0 12 0.0000 0 135 1575 540 990 Cforall Information\001 32 4 0 0 50 -1 0 12 0.0000 0 180 1695 540 585 _Unwind_Exception\001 33 4 0 0 50 -1 0 12 0.0000 0 180 1245 540 1530 User Exception\001 34 4 0 0 50 -1 0 12 0.0000 0 180 1185 2655 1530 Variable Body\001 35 4 0 0 50 -1 0 12 0.0000 0 165 1110 2655 1215 (Fixed Offset)\001 -
doc/theses/andrew_beach_MMath/existing.tex
rf95634e rb7fd9daf 6 6 compatibility with C and its programmers. \CFA is designed to have an 7 7 orthogonal feature-set based closely on the C programming paradigm 8 (non-object-oriented) and these features can be added incrementally to an 9 existing C code-base allowing programmers to learn \CFA on an as-needed basis. 8 (non-object-oriented), and these features can be added incrementally to an 9 existing C code-base, 10 allowing programmers to learn \CFA on an as-needed basis. 10 11 11 12 Only those \CFA features pertaining to this thesis are discussed. 12 % Also, only new features of \CFA will be discussed,13 13 A familiarity with 14 14 C or C-like languages is assumed. … … 17 17 \CFA has extensive overloading, allowing multiple definitions of the same name 18 18 to be defined~\cite{Moss18}. 19 \begin{ lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]20 char @i@; int @i@; double @i@;21 int @f@(); double @f@();22 void @g@( int ); void @g@( double );23 \end{ lstlisting}19 \begin{cfa} 20 char i; int i; double i; 21 int f(); double f(); 22 void g( int ); void g( double ); 23 \end{cfa} 24 24 This feature requires name mangling so the assembly symbols are unique for 25 25 different overloads. For compatibility with names in C, there is also a syntax … … 46 46 \CFA adds a reference type to C as an auto-dereferencing pointer. 47 47 They work very similarly to pointers. 48 Reference-types are written the same way as a pointer-typebut each48 Reference-types are written the same way as pointer-types, but each 49 49 asterisk (@*@) is replaced with a ampersand (@&@); 50 this includes cv-qualifiers and multiple levels of reference. 51 52 Generally, references act like pointers with an implicate dereferencing 50 this includes cv-qualifiers (\snake{const} and \snake{volatile}) 51 and multiple levels of reference. 52 53 Generally, references act like pointers with an implicit dereferencing 53 54 operation added to each use of the variable. 54 55 These automatic dereferences may be disabled with the address-of operator … … 63 64 int && rri = ri; 64 65 rri = 3; 65 &ri = &j; // rebindable66 &ri = &j; 66 67 ri = 5; 67 68 \end{cfa} … … 79 80 \end{minipage} 80 81 81 References are intended for pointer situations where dereferencing is the common usage, 82 \ie the value is more important than the pointer. 82 References are intended to be used when the indirection of a pointer is 83 required, but the address is not as important as the value and dereferencing 84 is the common usage. 83 85 Mutable references may be assigned to by converting them to a pointer 84 with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above 86 with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above. 85 87 86 88 \section{Operators} 87 89 88 90 \CFA implements operator overloading by providing special names, where 89 operator usages are translated into function calls using these names.91 operator expressions are translated into function calls using these names. 90 92 An operator name is created by taking the operator symbols and joining them with 91 93 @?@s to show where the arguments go. 92 94 For example, 93 95 infixed multiplication is @?*?@, while prefix dereference is @*?@. 94 This syntax make it easy to tell the difference between prefix operations 95 (such as @++?@) and post-fix operations (@?++@). 96 For example, plus and equality operators are defined for a point type. 96 This syntax makes it easy to tell the difference between prefix operations 97 (such as @++?@) and postfix operations (@?++@). 98 99 As an example, here are the addition and equality operators for a point type. 97 100 \begin{cfa} 98 101 point ?+?(point a, point b) { return point{a.x + b.x, a.y + b.y}; } … … 102 105 } 103 106 \end{cfa} 104 Note these special names are not limited to builtin 105 operators, and hence, may be used with arbitrary types. 106 \begin{cfa} 107 double ?+?( int x, point y ); // arbitrary types 108 \end{cfa} 109 % Some ``near misses", that are that do not match an operator form but looks like 110 % it may have been supposed to, will generate warning but otherwise they are 111 % left alone. 112 Because operators are never part of the type definition they may be added 113 at any time, including on built-in types. 107 Note that this syntax works effectively as a textual transformation; 108 the compiler converts all operators into functions and then resolves them 109 normally. This means any combination of types may be used, 110 although nonsensical ones (like @double ?==?(point, int);@) are discouraged. 111 This feature is also used for all builtin operators as well, 112 although those are implicitly provided by the language. 114 113 115 114 %\subsection{Constructors and Destructors} 116 117 \CFA also provides constructors and destructors as operators, which means they 118 are functions with special operator names rather than type names in \Cpp. 119 While constructors and destructions are normally called implicitly by the compiler, 120 the special operator names, allow explicit calls. 121 122 % Placement new means that this is actually equivalent to C++. 115 In \CFA, constructors and destructors are operators, which means they are 116 functions with special operator names, rather than type names as in \Cpp. 117 Both constructors and destructors can be implicity called by the compiler, 118 however the operator names allow explicit calls. 119 % Placement new means that this is actually equivant to C++. 123 120 124 121 The special name for a constructor is @?{}@, which comes from the … … 129 126 struct Example { ... }; 130 127 void ?{}(Example & this) { ... } 128 { 129 Example a; 130 Example b = {}; 131 } 131 132 void ?{}(Example & this, char first, int num) { ... } 132 Example a; // implicit constructor calls 133 Example b = {};134 Example c = {'a', 2}; 135 \end{cfa} 136 Both @a@ and @b@ are initialized with the first constructor,137 while @c@ is initialized with the second.138 Constructor calls can be replaced with C initialization using special operator \lstinline{@=}.139 \begin{cfa} 140 Example d @= {42}; 141 \end{cfa} 133 { 134 Example c = {'a', 2}; 135 } 136 \end{cfa} 137 Both @a@ and @b@ will be initalized with the first constructor, 138 @b@ because of the explicit call and @a@ implicitly. 139 @c@ will be initalized with the second constructor. 140 Currently, there is no general way to skip initialization. 141 % I don't use @= anywhere in the thesis. 142 142 143 % I don't like the \^{} symbol but $^\wedge$ isn't better. 143 144 Similarly, destructors use the special name @^?{}@ (the @^@ has no special 144 145 meaning). 145 % These are a normally called implicitly called on a variable when it goes out146 % of scope. They can be called explicitly as well.147 146 \begin{cfa} 148 147 void ^?{}(Example & this) { ... } 149 148 { 150 Example e; // implicit constructor call 151 ^?{}(e); // explicit destructor call 152 ?{}(e); // explicit constructor call 153 } // implicit destructor call 149 Example d; 150 ^?{}(d); 151 152 Example e; 153 } // Implicit call of ^?{}(e); 154 154 \end{cfa} 155 155 … … 203 203 do_twice(i); 204 204 \end{cfa} 205 Any objectwith a type fulfilling the assertion may be passed as an argument to205 Any value with a type fulfilling the assertion may be passed as an argument to 206 206 a @do_twice@ call. 207 207 … … 223 223 function. The matched assertion function is then passed as a function pointer 224 224 to @do_twice@ and called within it. 225 The global definition of @do_once@ is ignored, however if quadrupletook a225 The global definition of @do_once@ is ignored, however if @quadruple@ took a 226 226 @double@ argument, then the global definition would be used instead as it 227 is a better match. 228 % Aaron's thesis might be a good reference here. 229 230 To avoid typing long lists of assertions, constraints can be collect into 231 convenient package called a @trait@, which can then be used in an assertion 227 would then be a better match.\cite{Moss19} 228 229 To avoid typing long lists of assertions, constraints can be collected into 230 a convenient package called a @trait@, which can then be used in an assertion 232 231 instead of the individual constraints. 233 232 \begin{cfa} … … 243 242 functions and variables, and are usually used to create a shorthand for, and 244 243 give descriptive names to, common groupings of assertions describing a certain 245 functionality, like @sum able@, @listable@, \etc.244 functionality, like @summable@, @listable@, \etc. 246 245 247 246 Polymorphic structures and unions are defined by qualifying an aggregate type 248 247 with @forall@. The type variables work the same except they are used in field 249 declarations instead of parameters, returns ,and local variable declarations.248 declarations instead of parameters, returns and local variable declarations. 250 249 \begin{cfa} 251 250 forall(dtype T) … … 253 252 node(T) * next; 254 253 T * data; 255 } 254 }; 256 255 node(int) inode; 257 256 \end{cfa} … … 263 262 264 263 \section{Control Flow} 265 \CFA has a number of advanced control-flow features: @generator@, @coroutine@, @monitor@, @mutex@ parameters, and @thread@. 264 \CFA has a number of advanced control-flow features: @generator@, @coroutine@, 265 @monitor@, @mutex@ parameters, and @thread@. 266 266 The two features that interact with 267 267 the exception system are @coroutine@ and @thread@; they and their supporting … … 270 270 \subsection{Coroutine} 271 271 A coroutine is a type with associated functions, where the functions are not 272 required to finish execution when control is handed back to the caller. Instead 272 required to finish execution when control is handed back to the caller. 273 Instead, 273 274 they may suspend execution at any time and be resumed later at the point of 274 last suspension. (Generators are stackless and coroutines are stackful.) These 275 last suspension. 276 Coroutine 275 277 types are not concurrent but share some similarities along with common 276 underpinnings, so they are combined with the \CFA threading library. Further 277 discussion in this section only refers to the coroutine because generators are 278 similar. 278 underpinnings, so they are combined with the \CFA threading library. 279 % I had mention of generators, but they don't actually matter here. 279 280 280 281 In \CFA, a coroutine is created using the @coroutine@ keyword, which is an … … 293 294 }; 294 295 CountUp countup; 295 for (10) sout | resume(countup).next; // print 10 values296 296 \end{cfa} 297 297 Each coroutine has a @main@ function, which takes a reference to a coroutine 298 298 object and returns @void@. 299 299 %[numbers=left] Why numbers on this one? 300 \begin{cfa} [numbers=left,numberstyle=\scriptsize\sf]300 \begin{cfa} 301 301 void main(CountUp & this) { 302 for (unsigned int up = 0;; ++up) {303 this.next = up;302 for (unsigned int next = 0 ; true ; ++next) { 303 this.next = next; 304 304 suspend;$\label{suspend}$ 305 305 } … … 307 307 \end{cfa} 308 308 In this function, or functions called by this function (helper functions), the 309 @suspend@ statement is used to return execution to the coroutine's resumer310 without terminating the coroutine's function (s).309 @suspend@ statement is used to return execution to the coroutine's caller 310 without terminating the coroutine's function. 311 311 312 312 A coroutine is resumed by calling the @resume@ function, \eg @resume(countup)@. 313 313 The first resume calls the @main@ function at the top. Thereafter, resume calls 314 314 continue a coroutine in the last suspended function after the @suspend@ 315 statement, in this case @main@ line~\ref{suspend}. The @resume@ function takes 316 a reference to the coroutine structure and returns the same reference. The 317 return value allows easy access to communication variables defined in the 318 coroutine object. For example, the @next@ value for coroutine object @countup@ 319 is both generated and collected in the single expression: 320 @resume(countup).next@. 315 statement. In this case there is only one and, hence, the difference between 316 subsequent calls is the state of variables inside the function and the 317 coroutine object. 318 The return value of @resume@ is a reference to the coroutine, to make it 319 convent to access fields of the coroutine in the same expression. 320 Here is a simple example in a helper function: 321 \begin{cfa} 322 unsigned int get_next(CountUp & this) { 323 return resume(this).next; 324 } 325 \end{cfa} 326 327 When the main function returns, the coroutine halts and can no longer be 328 resumed. 321 329 322 330 \subsection{Monitor and Mutex Parameter} 323 Concurrency does not guarantee ordering; without ordering results are331 Concurrency does not guarantee ordering; without ordering, results are 324 332 non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@ 325 333 (mutual exclusion) parameters. A monitor is another kind of aggregate, where … … 327 335 @mutex@ parameters. 328 336 329 A function that requires deterministic (ordered) execution ,acquires mutual337 A function that requires deterministic (ordered) execution acquires mutual 330 338 exclusion on a monitor object by qualifying an object reference parameter with 331 @mutex@.332 \begin{ lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]333 void example(MonitorA & @mutex@ argA, MonitorB & @mutex@argB);334 \end{ lstlisting}339 the @mutex@ qualifier. 340 \begin{cfa} 341 void example(MonitorA & mutex argA, MonitorB & mutex argB); 342 \end{cfa} 335 343 When the function is called, it implicitly acquires the monitor lock for all of 336 344 the mutex parameters without deadlock. This semantics means all functions with … … 339 347 340 348 \subsection{Thread} 341 Functions, generators , and coroutines are sequentialso there is only a single349 Functions, generators and coroutines are sequential, so there is only a single 342 350 (but potentially sophisticated) execution path in a program. Threads introduce 343 351 multiple execution paths that continue independently. 344 352 345 353 For threads to work safely with objects requires mutual exclusion using 346 monitors and mutex parameters. For threads to work safely with other threads ,354 monitors and mutex parameters. For threads to work safely with other threads 347 355 also requires mutual exclusion in the form of a communication rendezvous, which 348 356 also supports internal synchronization as for mutex objects. For exceptions, … … 362 370 { 363 371 StringWorker stringworker; // fork thread running in "main" 364 } // implicitly join with thread / wait for completion372 } // Implicit call to join(stringworker), waits for completion. 365 373 \end{cfa} 366 374 The thread main is where a new thread starts execution after a fork operation -
doc/theses/andrew_beach_MMath/features.tex
rf95634e rb7fd9daf 5 5 and begins with a general overview of EHMs. It is not a strict 6 6 definition of all EHMs nor an exhaustive list of all possible features. 7 However it does cover the most common structure and features found in them.7 However, it does cover the most common structure and features found in them. 8 8 9 9 \section{Overview of EHMs} … … 19 19 20 20 \paragraph{Raise} 21 The raise is the starting point for exception handling 21 The raise is the starting point for exception handling, 22 22 by raising an exception, which passes it to 23 23 the EHM. … … 30 30 \paragraph{Handle} 31 31 The primary purpose of an EHM is to run some user code to handle a raised 32 exception. This code is given, with some other information, in a handler. 32 exception. This code is given, along with some other information, 33 in a handler. 33 34 34 35 A handler has three common features: the previously mentioned user code, a 35 region of code it guards ,and an exception label/condition that matches36 the raised exception.36 region of code it guards and an exception label/condition that matches 37 against the raised exception. 37 38 Only raises inside the guarded region and raising exceptions that match the 38 39 label can be handled by a given handler. … … 41 42 42 43 The @try@ statements of \Cpp, Java and Python are common examples. All three 43 show the common features of guarded region, raise, matching and handler. 44 \begin{cfa} 45 try { // guarded region 46 ... 47 throw exception; // raise 48 ... 49 } catch( exception ) { // matching condition, with exception label 50 ... // handler code 51 } 52 \end{cfa} 44 also show another common feature of handlers: they are grouped by the guarded 45 region. 53 46 54 47 \subsection{Propagation} 55 48 After an exception is raised comes what is usually the biggest step for the 56 EHM: finding and setting up the handler for execution. The propagation from raise to 49 EHM: finding and setting up the handler for execution. 50 The propagation from raise to 57 51 handler can be broken up into three different tasks: searching for a handler, 58 52 matching against the handler and installing the handler. … … 60 54 \paragraph{Searching} 61 55 The EHM begins by searching for handlers that might be used to handle 62 the exception. The search is restricted to63 handlers that have the raise site in their guarded56 the exception. 57 The search will find handlers that have the raise site in their guarded 64 58 region. 65 59 The search includes handlers in the current function, as well as any in … … 67 61 68 62 \paragraph{Matching} 69 Each handler found is matchedwith the raised exception. The exception63 Each handler found is with the raised exception. The exception 70 64 label defines a condition that is used with the exception and decides if 71 65 there is a match or not. 66 % 72 67 In languages where the first match is used, this step is intertwined with 73 68 searching; a match check is performed immediately after the search finds … … 84 79 different course of action for this case. 85 80 This situation only occurs with unchecked exceptions as checked exceptions 86 (such as in Java) are guaranteed to find a matching handler.81 (such as in Java) can make the guarantee. 87 82 The unhandled action is usually very general, such as aborting the program. 88 83 89 84 \paragraph{Hierarchy} 90 85 A common way to organize exceptions is in a hierarchical structure. 91 This pattern comes from object-orient ated languages where the86 This pattern comes from object-oriented languages where the 92 87 exception hierarchy is a natural extension of the object hierarchy. 93 88 … … 98 93 A handler labeled with any given exception can handle exceptions of that 99 94 type or any child type of that exception. The root of the exception hierarchy 100 (here \code{C}{exception}) acts as a catch-all, leaf types catch single types ,95 (here \code{C}{exception}) acts as a catch-all, leaf types catch single types 101 96 and the exceptions in the middle can be used to catch different groups of 102 97 related exceptions. 103 98 104 99 This system has some notable advantages, such as multiple levels of grouping, 105 the ability for libraries to add new exception types ,and the isolation100 the ability for libraries to add new exception types and the isolation 106 101 between different sub-hierarchies. 107 102 This design is used in \CFA even though it is not a object-orientated 108 language ;so different tools are used to create the hierarchy.103 language, so different tools are used to create the hierarchy. 109 104 110 105 % Could I cite the rational for the Python IO exception rework? … … 124 119 from the raise to the handler and back again. 125 120 So far, only communication of the exception's identity is covered. 126 A common communication method for passing more information is putting fields into the exception instance 121 A common communication method for adding information to an exception 122 is putting fields into the exception instance 127 123 and giving the handler access to them. 128 Using reference fields pointing to data at the raise location allows data to be 129 passed in both directions. 124 % You can either have pointers/references in the exception, or have p/rs to 125 % the exception when it doesn't have to be copied. 126 Passing references or pointers allows data at the raise location to be 127 updated, passing information in both directions. 130 128 131 129 \section{Virtuals} 132 \label{s:Virtuals} 130 \label{s:virtuals} 131 A common feature in many programming languages is a tool to pair code 132 (behaviour) with data. 133 In \CFA, this is done with the virtual system, 134 which allow type information to be abstracted away, recovered and allow 135 operations to be performed on the abstract objects. 136 133 137 Virtual types and casts are not part of \CFA's EHM nor are they required for 134 138 an EHM. 135 139 However, one of the best ways to support an exception hierarchy 136 140 is via a virtual hierarchy and dispatch system. 137 Ideally, the virtual system should have been part of \CFA before the work141 Ideally, the virtual system would have been part of \CFA before the work 138 142 on exception handling began, but unfortunately it was not. 139 143 Hence, only the features and framework needed for the EHM were 140 designed and implemented for this thesis. Other features were considered to ensure that 144 designed and implemented for this thesis. 145 Other features were considered to ensure that 141 146 the structure could accommodate other desirable features in the future 142 147 but are not implemented. 143 148 The rest of this section only discusses the implemented subset of the 144 virtual -system design.149 virtual system design. 145 150 146 151 The virtual system supports multiple ``trees" of types. Each tree is … … 149 154 number of children. 150 155 Any type that belongs to any of these trees is called a virtual type. 151 For example, the following hypothetical syntax creates two virtual-type trees.152 \begin{flushleft}153 \lstDeleteShortInline@154 \begin{tabular}{@{\hspace{20pt}}l@{\hspace{20pt}}l}155 \begin{cfa}156 vtype V0, V1(V0), V2(V0);157 vtype W0, W1(W0), W2(W1);158 \end{cfa}159 &160 \raisebox{-0.6\totalheight}{\input{vtable}}161 \end{tabular}162 \lstMakeShortInline@163 \end{flushleft}164 156 % A type's ancestors are its parent and its parent's ancestors. 165 157 % The root type has no ancestors. 166 158 % A type's descendants are its children and its children's descendants. 167 Every virtual type (tree node) has a pointer to a virtual table with a unique 168 @Id@ and a list of virtual members (see \autoref{s:VirtualSystem} for 169 details). Children inherit their parent's list of virtual members but may add 170 and/or replace members. For example, 171 \begin{cfa} 172 vtable W0 | { int ?<?( int, int ); int ?+?( int, int ); } 173 vtable W1 | { int ?+?( int, int ); int w, int ?-?( int, int ); } 174 \end{cfa} 175 creates a virtual table for @W0@ initialized with the matching @<@ and @+@ 176 operations visible at this declaration context. Similarly, @W1@ is initialized 177 with @<@ from inheritance with @W0@, @+@ is replaced, and @-@ is added, where 178 both operations are matched at this declaration context. It is important to 179 note that these are virtual members, not virtual methods of object-orientated 180 programming, and can be of any type. Finally, trait names can be used to 181 specify the list of virtual members. 182 183 \PAB{Need to look at these when done. 184 185 \CFA still supports virtual methods as a special case of virtual members. 186 Function pointers that take a pointer to the virtual type are modified 187 with each level of inheritance so that refers to the new type. 188 This means an object can always be passed to a function in its virtual table 189 as if it were a method. 190 \todo{Clarify (with an example) virtual methods.} 191 }% 192 193 Up until this point the virtual system is similar to ones found in 194 object-orientated languages but this is where \CFA diverges. Objects encapsulate a 195 single set of methods in each type, universally across the entire program, 196 and indeed all programs that use that type definition. Even if a type inherits and adds methods, it still encapsulate a 197 single set of methods. In this sense, 198 object-oriented types are ``closed" and cannot be altered. 199 200 In \CFA, types do not encapsulate any code. Traits are local for each function and 201 types can satisfy a local trait, stop satisfying it or, satisfy the same 202 trait in a different way at any lexical location in the program where a function is call. 203 In this sense, the set of functions/variables that satisfy a trait for a type is ``open" as the set can change at every call site. 159 160 For the purposes of illustration, a proposed, but unimplemented, syntax 161 will be used. Each virtual type is represented by a trait with an annotation 162 that makes it a virtual type. This annotation is empty for a root type, which 163 creates a new tree: 164 \begin{cfa} 165 trait root_type(T) virtual() {} 166 \end{cfa} 167 The annotation may also refer to any existing virtual type to make this new 168 type a child of that type and part of the same tree. The parent may itself 169 be a child or a root type and may have any number of existing children. 170 171 % OK, for some reason the b and t positioning options are reversed here. 172 \begin{minipage}[b]{0.6\textwidth} 173 \begin{cfa} 174 trait child_a(T) virtual(root_type) {} 175 trait grandchild(T) virtual(child_a) {} 176 trait child_b(T) virtual(root_type) {} 177 \end{cfa} 178 \end{minipage} 179 \begin{minipage}{0.4\textwidth} 180 \begin{center} 181 \input{virtual-tree} 182 \end{center} 183 \end{minipage} 184 185 Every virtual type also has a list of virtual members and a unique id. 186 Both are stored in a virtual table. 187 Every instance of a virtual type also has a pointer to a virtual table stored 188 in it, although there is no per-type virtual table as in many other languages. 189 190 The list of virtual members is accumulated from the root type down the tree. 191 Every virtual type 192 inherits the list of virtual members from its parent and may add more 193 virtual members to the end of the list which are passed on to its children. 194 Again, using the unimplemented syntax this might look like: 195 \begin{cfa} 196 trait root_type(T) virtual() { 197 const char * to_string(T const & this); 198 unsigned int size; 199 } 200 201 trait child_type(T) virtual(root_type) { 202 char * irrelevant_function(int, char); 203 } 204 \end{cfa} 205 % Consider adding a diagram, but we might be good with the explanation. 206 207 As @child_type@ is a child of @root_type@, it has the virtual members of 208 @root_type@ (@to_string@ and @size@) as well as the one it declared 209 (@irrelevant_function@). 210 211 It is important to note that these are virtual members, and may contain 212 arbitrary fields, functions or otherwise. 213 The names ``size" and ``align" are reserved for the size and alignment of the 214 virtual type, and are always automatically initialized as such. 215 The other special case is uses of the trait's polymorphic argument 216 (@T@ in the example), which are always updated to refer to the current 217 virtual type. This allows functions that refer to the polymorphic argument 218 to act as traditional virtual methods (@to_string@ in the example), as the 219 object can always be passed to a virtual method in its virtual table. 220 221 Up until this point, the virtual system is similar to ones found in 222 object-oriented languages, but this is where \CFA diverges. 223 Objects encapsulate a single set of methods in each type, 224 universally across the entire program, 225 and indeed all programs that use that type definition. 226 The only way to change any method is to inherit and define a new type with 227 its own universal implementation. In this sense, 228 these object-oriented types are ``closed" and cannot be altered. 229 % Because really they are class oriented. 230 231 In \CFA, types do not encapsulate any code. 232 Whether or not a type satisfies any given assertion, and hence any trait, is 233 context sensitive. Types can begin to satisfy a trait, stop satisfying it or 234 satisfy the same trait at any lexical location in the program. 235 In this sense, a type's implementation in the set of functions and variables 236 that allow it to satisfy a trait is ``open" and can change 237 throughout the program. 204 238 This capability means it is impossible to pick a single set of functions 205 239 that represent a type's implementation across a program. … … 208 242 type. A user can define virtual tables that are filled in at their 209 243 declaration and given a name. Anywhere that name is visible, even if it is 210 defined locally inside a function \PAB{What does this mean? (although that means it does not have a211 static lifetime)}, it can be used.244 defined locally inside a function (although in this case the user must ensure 245 it outlives any objects that use it), it can be used. 212 246 Specifically, a virtual type is ``bound" to a virtual table that 213 247 sets the virtual members for that object. The virtual members can be accessed 214 248 through the object. 215 249 216 While much of the virtual infrastructure is created, it is currently only used 250 This means virtual tables are declared and named in \CFA. 251 They are declared as variables, using the type 252 @vtable(VIRTUAL_TYPE)@ and any valid name. For example: 253 \begin{cfa} 254 vtable(virtual_type_name) table_name; 255 \end{cfa} 256 257 Like any variable, they may be forward declared with the @extern@ keyword. 258 Forward declaring virtual tables is relatively common. 259 Many virtual types have an ``obvious" implementation that works in most 260 cases. 261 A pattern that has appeared in the early work using virtuals is to 262 implement a virtual table with the the obvious definition and place a forward 263 declaration of it in the header beside the definition of the virtual type. 264 265 Even on the full declaration, no initializer should be used. 266 Initialization is automatic. 267 The type id and special virtual members ``size" and ``align" only depend on 268 the virtual type, which is fixed given the type of the virtual table, and 269 so the compiler fills in a fixed value. 270 The other virtual members are resolved using the best match to the member's 271 name and type, in the same context as the virtual table is declared using 272 \CFA's normal resolution rules. 273 274 While much of the virtual infrastructure has been created, 275 it is currently only used 217 276 internally for exception handling. The only user-level feature is the virtual 218 277 cast, which is the same as the \Cpp \code{C++}{dynamic_cast}. … … 223 282 Note, the syntax and semantics matches a C-cast, rather than the function-like 224 283 \Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be 225 a pointer to a virtual type.284 pointers to virtual types. 226 285 The cast dynamically checks if the @EXPRESSION@ type is the same or a sub-type 227 286 of @TYPE@, and if true, returns a pointer to the 228 287 @EXPRESSION@ object, otherwise it returns @0p@ (null pointer). 229 230 \section{Exception} 231 % Leaving until later, hopefully it can talk about actual syntax instead 232 % of my many strange macros. Syntax aside I will also have to talk about the 233 % features all exceptions support. 234 235 Exceptions are defined by the trait system; there are a series of traits, and 236 if a type satisfies them, then it can be used as an exception. The following 288 This allows the expression to be used as both a cast and a type check. 289 290 \section{Exceptions} 291 292 The syntax for declaring an exception is the same as declaring a structure 293 except the keyword: 294 \begin{cfa} 295 exception TYPE_NAME { 296 FIELDS 297 }; 298 \end{cfa} 299 300 Fields are filled in the same way as a structure as well. However, an extra 301 field is added that contains the pointer to the virtual table. 302 It must be explicitly initialized by the user when the exception is 303 constructed. 304 305 Here is an example of declaring an exception type along with a virtual table, 306 assuming the exception has an ``obvious" implementation and a default 307 virtual table makes sense. 308 309 \begin{minipage}[t]{0.4\textwidth} 310 Header (.hfa): 311 \begin{cfa} 312 exception Example { 313 int data; 314 }; 315 316 extern vtable(Example) 317 example_base_vtable; 318 \end{cfa} 319 \end{minipage} 320 \begin{minipage}[t]{0.6\textwidth} 321 Implementation (.cfa): 322 \begin{cfa} 323 vtable(Example) example_base_vtable 324 \end{cfa} 325 \vfil 326 \end{minipage} 327 328 %\subsection{Exception Details} 329 This is the only interface needed when raising and handling exceptions. 330 However, it is actually a shorthand for a more complex 331 trait-based interface. 332 333 The language views exceptions through a series of traits. 334 If a type satisfies them, then it can be used as an exception. The following 237 335 is the base trait all exceptions need to match. 238 336 \begin{cfa} … … 241 339 }; 242 340 \end{cfa} 243 The trait is defined over two types ,the exception type and the virtual table341 The trait is defined over two types: the exception type and the virtual table 244 342 type. Each exception type should have a single virtual table type. 245 343 There are no actual assertions in this trait because the trait system … … 247 345 completing the virtual system). The imaginary assertions would probably come 248 346 from a trait defined by the virtual system, and state that the exception type 249 is a virtual type, is a descendant of @exception_t@ (the base exception type), 250 and note its virtual table type. 347 is a virtual type, 348 that that the type is a descendant of @exception_t@ (the base exception type) 349 and allow the user to find the virtual table type. 251 350 252 351 % I did have a note about how it is the programmer's responsibility to make … … 266 365 }; 267 366 \end{cfa} 268 Both traits ensure a pair of types are an exception type, its virtual table269 type,367 Both traits ensure a pair of types is an exception type and 368 its virtual table type, 270 369 and defines one of the two default handlers. The default handlers are used 271 as fallbacks and are discussed in detail in \ vref{s:ExceptionHandling}.370 as fallbacks and are discussed in detail in \autoref{s:ExceptionHandling}. 272 371 273 372 However, all three of these traits can be tricky to use directly. 274 373 While there is a bit of repetition required, 275 374 the largest issue is that the virtual table type is mangled and not in a user 276 facing way. So these three macros are provided to wrap these traits to375 facing way. So, these three macros are provided to wrap these traits to 277 376 simplify referring to the names: 278 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ ,and @IS_RESUMPTION_EXCEPTION@.377 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. 279 378 280 379 All three take one or two arguments. The first argument is the name of the … … 282 381 The second (optional) argument is a parenthesized list of polymorphic 283 382 arguments. This argument is only used with polymorphic exceptions and the 284 list is bepassed to both types.383 list is passed to both types. 285 384 In the current set-up, the two types always have the same polymorphic 286 arguments so these macros can be used without losing flexibility.287 288 For example consider a function that is polymorphic over types that have a385 arguments, so these macros can be used without losing flexibility. 386 387 For example, consider a function that is polymorphic over types that have a 289 388 defined arithmetic exception: 290 389 \begin{cfa} … … 303 402 Both operations follow the same set of steps. 304 403 First, a user raises an exception. 305 Second, the exception propagates up the stack .404 Second, the exception propagates up the stack, searching for a handler. 306 405 Third, if a handler is found, the exception is caught and the handler is run. 307 406 After that control continues at a raise-dependent location. 308 Fourth, if a handler is not found, a default handler is run and, if it returns, then control 407 As an alternate to the third step, 408 if a handler is not found, a default handler is run and, if it returns, 409 then control 309 410 continues after the raise. 310 411 311 %This general description covers what the two kinds have in common. 312 The differences in the two operations include how propagation is performed, where execution continues 313 a fter an exception is caught and handled, and which default handler is run.412 The differences between the two operations include how propagation is 413 performed, where execution continues after an exception is handled 414 and which default handler is run. 314 415 315 416 \subsection{Termination} 316 417 \label{s:Termination} 317 Termination handling is the familiar EHM and used in most programming 418 Termination handling is the familiar kind of handling 419 used in most programming 318 420 languages with exception handling. 319 421 It is a dynamic, non-local goto. If the raised exception is matched and … … 347 449 Then propagation starts with the search. \CFA uses a ``first match" rule so 348 450 matching is performed with the copied exception as the search key. 349 It starts from the raise in the throwing function and proceeds towards thebase of the stack,451 It starts from the raise site and proceeds towards base of the stack, 350 452 from callee to caller. 351 453 At each stack frame, a check is made for termination handlers defined by the … … 361 463 \end{cfa} 362 464 When viewed on its own, a try statement simply executes the statements 363 in the \snake{GUARDED_BLOCK} ,and when those are finished,465 in the \snake{GUARDED_BLOCK} and when those are finished, 364 466 the try statement finishes. 365 467 … … 387 489 termination exception types. 388 490 The global default termination handler performs a cancellation 389 (see \vref{s:Cancellation} for the justification) on the current stack with the copied exception. 390 Since it is so general, a more specific handler is usually 391 defined, possibly with a detailed message, and used for specific exception type, effectively overriding the default handler. 491 (as described in \vref{s:Cancellation}) 492 on the current stack with the copied exception. 493 Since it is so general, a more specific handler can be defined, 494 overriding the default behaviour for the specific exception types. 495 496 For example, consider an error reading a configuration file. 497 This is most likely a problem with the configuration file (@config_error@), 498 but the function could have been passed the wrong file name (@arg_error@). 499 In this case the function could raise one exception and then, if it is 500 unhandled, raise the other. 501 This is not usual behaviour for either exception so changing the 502 default handler will be done locally: 503 \begin{cfa} 504 { 505 void defaultTerminationHandler(config_error &) { 506 throw (arg_error){arg_vt}; 507 } 508 throw (config_error){config_vt}; 509 } 510 \end{cfa} 392 511 393 512 \subsection{Resumption} 394 513 \label{s:Resumption} 395 514 396 Resumption exception handling is the less familar EHM, but is 515 Resumption exception handling is less familar form of exception handling, 516 but is 397 517 just as old~\cite{Goodenough75} and is simpler in many ways. 398 518 It is a dynamic, non-local function call. If the raised exception is … … 403 523 function once the error is corrected, and 404 524 ignorable events, such as logging where nothing needs to happen and control 405 should always continue from the raise point. 525 should always continue from the raise site. 526 527 Except for the changes to fit into that pattern, resumption exception 528 handling is symmetric with termination exception handling, by design 529 (see \autoref{s:Termination}). 406 530 407 531 A resumption raise is started with the @throwResume@ statement: … … 409 533 throwResume EXPRESSION; 410 534 \end{cfa} 411 \todo{Decide on a final set of keywords and use them everywhere.} 412 It works much the same way as the termination throw. 413 The expression must return a reference to a resumption exception, 414 where the resumption exception is any type that satisfies the trait 415 @is_resumption_exception@ at the call site. 416 The assertions from this trait are available to 417 the exception system while handling the exception. 418 419 At run-time, no exception copy is made, since 535 % The new keywords are currently ``experimental" and not used in this work. 536 It works much the same way as the termination raise, except the 537 type must satisfy the \snake{is_resumption_exception} that uses the 538 default handler: \defaultResumptionHandler. 539 This can be specialized for particular exception types. 540 541 At run-time, no exception copy is made. Since 420 542 resumption does not unwind the stack nor otherwise remove values from the 421 current scope, so there is no need to manage memory to keep the exception in scope.422 423 Then propagation starts with the search. It starts from the raise in the 424 resuming function and proceeds towards the base of the stack,425 f rom callee to caller.426 At each stack frame, a check is made for resumption handlers defined by the 427 @catchResume@ clauses of a @try@ statement.543 current scope, there is no need to manage memory to keep the exception 544 allocated. 545 546 Then propagation starts with the search, 547 following the same search path as termination, 548 from the raise site to the base of stack and top of try statement to bottom. 549 However, the handlers on try statements are defined by @catchResume@ clauses. 428 550 \begin{cfa} 429 551 try { … … 435 557 } 436 558 \end{cfa} 437 % PAB, you say this above. 438 % When a try statement is executed, it simply executes the statements in the 439 % @GUARDED_BLOCK@ and then finishes. 440 % 441 % However, while the guarded statements are being executed, including any 442 % invoked functions, all the handlers in these statements are included in the 443 % search path. 444 % Hence, if a resumption exception is raised, these handlers may be matched 445 % against the exception and may handle it. 446 % 447 % Exception matching checks the handler in each catch clause in the order 448 % they appear, top to bottom. If the representation of the raised exception type 449 % is the same or a descendant of @EXCEPTION_TYPE@$_i$, then @NAME@$_i$ 450 % (if provided) is bound to a pointer to the exception and the statements in 451 % @HANDLER_BLOCK@$_i$ are executed. 452 % If control reaches the end of the handler, execution continues after the 453 % the raise statement that raised the handled exception. 454 % 455 % Like termination, if no resumption handler is found during the search, 456 % then the default handler (\defaultResumptionHandler) visible at the raise 457 % statement is called. It will use the best match at the raise sight according 458 % to \CFA's overloading rules. The default handler is 459 % passed the exception given to the raise. When the default handler finishes 460 % execution continues after the raise statement. 461 % 462 % There is a global @defaultResumptionHandler{} is polymorphic over all 463 % resumption exceptions and performs a termination throw on the exception. 464 % The \defaultTerminationHandler{} can be overridden by providing a new 465 % function that is a better match. 466 467 The @GUARDED_BLOCK@ and its associated nested guarded statements work the same 468 for resumption as for termination, as does exception matching at each 469 @catchResume@. Similarly, if no resumption handler is found during the search, 470 then the currently visible default handler (\defaultResumptionHandler) is 471 called and control continues after the raise statement if it returns. Finally, 472 there is also a global @defaultResumptionHandler@, which can be overridden, 473 that is polymorphic over all resumption exceptions but performs a termination 474 throw on the exception rather than a cancellation. 475 476 Throwing the exception in @defaultResumptionHandler@ has the positive effect of 477 walking the stack a second time for a recovery handler. Hence, a programmer has 478 two chances for help with a problem, fixup or recovery, should either kind of 479 handler appear on the stack. However, this dual stack walk leads to following 480 apparent anomaly: 481 \begin{cfa} 482 try { 483 throwResume E; 484 } catch (E) { 485 // this handler runs 486 } 487 \end{cfa} 488 because the @catch@ appears to handle a @throwResume@, but a @throwResume@ only 489 matches with @catchResume@. The anomaly results because the unmatched 490 @catchResuem@, calls @defaultResumptionHandler@, which in turn throws @E@. 491 492 % I wonder if there would be some good central place for this. 493 Note, termination and resumption handlers may be used together 559 Note that termination handlers and resumption handlers may be used together 494 560 in a single try statement, intermixing @catch@ and @catchResume@ freely. 495 561 Each type of handler only interacts with exceptions from the matching 496 562 kind of raise. 563 Like @catch@ clauses, @catchResume@ clauses have no effect if an exception 564 is not raised. 565 566 The matching rules are exactly the same as well. 567 The first major difference here is that after 568 @EXCEPTION_TYPE@$_i$ is matched and @NAME@$_i$ is bound to the exception, 569 @HANDLER_BLOCK@$_i$ is executed right away without first unwinding the stack. 570 After the block has finished running, control jumps to the raise site, where 571 the just handled exception came from, and continues executing after it, 572 not after the try statement. 573 574 For instance, a resumption used to send messages to the logger may not 575 need to be handled at all. Putting the following default handler 576 at the global scope can make handling that exception optional by default. 577 \begin{cfa} 578 void defaultResumptionHandler(log_message &) { 579 // Nothing, it is fine not to handle logging. 580 } 581 // ... No change at raise sites. ... 582 throwResume (log_message){strlit_log, "Begin event processing."} 583 \end{cfa} 497 584 498 585 \subsubsection{Resumption Marking} … … 501 588 not unwind the stack. A side effect is that, when a handler is matched 502 589 and run, its try block (the guarded statements) and every try statement 503 searched before it are still on the stack. There presence can lead to 504 the \emph{recursive resumption problem}. 590 searched before it are still on the stack. Their presence can lead to 591 the recursive resumption problem.\cite{Buhr00a} 592 % Other possible citation is MacLaren77, but the form is different. 505 593 506 594 The recursive resumption problem is any situation where a resumption handler … … 516 604 When this code is executed, the guarded @throwResume@ starts a 517 605 search and matches the handler in the @catchResume@ clause. This 518 call is placed on the stack above the try-block. Now the second raise in the handler 519 searches the same try block, matches, and puts another instance of the 606 call is placed on the stack above the try-block. 607 Now the second raise in the handler searches the same try block, 608 matches again and then puts another instance of the 520 609 same handler on the stack leading to infinite recursion. 521 610 522 While this situation is trivial and easy to avoid, much more complex cycles can 523 form with multiple handlers and different exception types. The key point is 524 that the programmer's intuition expects every raise in a handler to start 525 searching \emph{below} the @try@ statement, making it difficult to understand 526 and fix the problem. 527 611 While this situation is trivial and easy to avoid, much more complex cycles 612 can form with multiple handlers and different exception types. 528 613 To prevent all of these cases, each try statement is ``marked" from the 529 time the exception search reaches it to either when a matching handler530 completesor when the search reaches the base614 time the exception search reaches it to either when a handler completes 615 handling that exception or when the search reaches the base 531 616 of the stack. 532 617 While a try statement is marked, its handlers are never matched, effectively … … 537 622 \end{center} 538 623 539 There are other sets of marking rules that could be used ,540 for instance, marking just the handlers that caught the exception, 624 There are other sets of marking rules that could be used. 625 For instance, marking just the handlers that caught the exception 541 626 would also prevent recursive resumption. 542 However, the rule selected mirrorswhat happens with termination,543 and hence, matches programmer intuition that a raise searches below a try.544 545 In detail, the marked try statements are the ones that would be removed from627 However, the rules selected mirror what happens with termination, 628 so this reduces the amount of rules and patterns a programmer has to know. 629 630 The marked try statements are the ones that would be removed from 546 631 the stack for a termination exception, \ie those on the stack 547 632 between the handler and the raise statement. … … 580 665 // Handle a failure relating to f2 further down the stack. 581 666 \end{cfa} 582 In this example the file that experienced the IO error is used to decide667 In this example, the file that experienced the IO error is used to decide 583 668 which handler should be run, if any at all. 584 669 … … 609 694 610 695 \subsection{Comparison with Reraising} 611 Without conditional catch, the only approach to match in more detail is to reraise 612 the exception after it has been caught, if it could not be handled. 696 In languages without conditional catch -- that is, no ability to match an 697 exception based on something other than its type -- it can be mimicked 698 by matching all exceptions of the right type, checking any additional 699 conditions inside the handler and re-raising the exception if it does not 700 match those. 701 702 Here is a minimal example comparing both patterns, using @throw;@ 703 (no operand) to start a re-raise. 613 704 \begin{center} 614 \begin{tabular}{l |l}705 \begin{tabular}{l r} 615 706 \begin{cfa} 616 707 try { 617 do_work_may_throw();618 } catch(excep _t * ex; can_handle(ex)) {619 620 handle(ex);621 622 623 624 } 708 do_work_may_throw(); 709 } catch(exception_t * exc ; 710 can_handle(exc)) { 711 handle(exc); 712 } 713 714 715 625 716 \end{cfa} 626 717 & 627 718 \begin{cfa} 628 719 try { 629 do_work_may_throw(); 630 } catch(excep_t * ex) { 631 if (can_handle(ex)) { 632 handle(ex); 720 do_work_may_throw(); 721 } catch(exception_t * exc) { 722 if (can_handle(exc)) { 723 handle(exc); 724 } else { 725 throw; 726 } 727 } 728 \end{cfa} 729 \end{tabular} 730 \end{center} 731 At first glance, catch-and-reraise may appear to just be a quality-of-life 732 feature, but there are some significant differences between the two 733 strategies. 734 735 A simple difference that is more important for \CFA than many other languages 736 is that the raise site changes with a re-raise, but does not with a 737 conditional catch. 738 This is important in \CFA because control returns to the raise site to run 739 the per-site default handler. Because of this, only a conditional catch can 740 allow the original raise to continue. 741 742 The more complex issue comes from the difference in how conditional 743 catches and re-raises handle multiple handlers attached to a single try 744 statement. A conditional catch will continue checking later handlers while 745 a re-raise will skip them. 746 If the different handlers could handle some of the same exceptions, 747 translating a try statement that uses one to use the other can quickly 748 become non-trivial: 749 750 \noindent 751 Original, with conditional catch: 752 \begin{cfa} 753 ... 754 } catch (an_exception * e ; check_a(e)) { 755 handle_a(e); 756 } catch (exception_t * e ; check_b(e)) { 757 handle_b(e); 758 } 759 \end{cfa} 760 Translated, with re-raise: 761 \begin{cfa} 762 ... 763 } catch (exception_t * e) { 764 an_exception * an_e = (virtual an_exception *)e; 765 if (an_e && check_a(an_e)) { 766 handle_a(an_e); 767 } else if (check_b(e)) { 768 handle_b(e); 633 769 } else { 634 770 throw; … … 636 772 } 637 773 \end{cfa} 638 \end{tabular} 639 \end{center} 640 Notice catch-and-reraise increases complexity by adding additional data and 641 code to the exception process. Nevertheless, catch-and-reraise can simulate 642 conditional catch straightforwardly, when exceptions are disjoint, \ie no 643 inheritance. 644 645 However, catch-and-reraise simulation becomes unusable for exception inheritance. 646 \begin{flushleft} 647 \begin{cfa}[xleftmargin=6pt] 648 exception E1; 649 exception E2(E1); // inheritance 650 \end{cfa} 651 \begin{tabular}{l|l} 652 \begin{cfa} 653 try { 654 ... foo(); ... // raise E1/E2 655 ... bar(); ... // raise E1/E2 656 } catch( E2 e; e.rtn == foo ) { 657 ... 658 } catch( E1 e; e.rtn == foo ) { 659 ... 660 } catch( E1 e; e.rtn == bar ) { 661 ... 662 } 663 664 \end{cfa} 665 & 666 \begin{cfa} 667 try { 668 ... foo(); ... 669 ... bar(); ... 670 } catch( E2 e ) { 671 if ( e.rtn == foo ) { ... 672 } else throw; // reraise 673 } catch( E1 e ) { 674 if (e.rtn == foo) { ... 675 } else if (e.rtn == bar) { ... 676 else throw; // reraise 677 } 678 \end{cfa} 679 \end{tabular} 680 \end{flushleft} 681 The derived exception @E2@ must be ordered first in the catch list, otherwise 682 the base exception @E1@ catches both exceptions. In the catch-and-reraise code 683 (right), the @E2@ handler catches exceptions from both @foo@ and 684 @bar@. However, the reraise misses the following catch clause. To fix this 685 problem, an enclosing @try@ statement is need to catch @E2@ for @bar@ from the 686 reraise, and its handler must duplicate the inner handler code for @bar@. To 687 generalize, this fix for any amount of inheritance and complexity of try 688 statement requires a technique called \emph{try-block 689 splitting}~\cite{Krischer02}, which is not discussed in this thesis. It is 690 sufficient to state that conditional catch is more expressive than 691 catch-and-reraise in terms of complexity. 692 693 \begin{comment} 694 That is, they have the same behaviour in isolation. 695 Two things can expose differences between these cases. 696 697 One is the existence of multiple handlers on a single try statement. 698 A reraise skips all later handlers for a try statement but a conditional 699 catch does not. 700 % Hence, if an earlier handler contains a reraise later handlers are 701 % implicitly skipped, with a conditional catch they are not. 702 Still, they are equivalently powerful, 703 both can be used two mimic the behaviour of the other, 704 as reraise can pack arbitrary code in the handler and conditional catches 705 can put arbitrary code in the predicate. 706 % I was struggling with a long explanation about some simple solutions, 707 % like repeating a condition on later handlers, and the general solution of 708 % merging everything together. I don't think it is useful though unless its 709 % for a proof. 710 % https://en.cppreference.com/w/cpp/language/throw 711 712 The question then becomes ``Which is a better default?" 713 We believe that not skipping possibly useful handlers is a better default. 714 If a handler can handle an exception it should and if the handler can not 715 handle the exception then it is probably safer to have that explicitly 716 described in the handler itself instead of implicitly described by its 717 ordering with other handlers. 718 % Or you could just alter the semantics of the throw statement. The handler 719 % index is in the exception so you could use it to know where to start 720 % searching from in the current try statement. 721 % No place for the `goto else;` metaphor. 722 723 The other issue is all of the discussion above assumes that the only 724 way to tell apart two raises is the exception being raised and the remaining 725 search path. 726 This is not true generally, the current state of the stack can matter in 727 a number of cases, even only for a stack trace after an program abort. 728 But \CFA has a much more significant need of the rest of the stack, the 729 default handlers for both termination and resumption. 730 731 % For resumption it turns out it is possible continue a raise after the 732 % exception has been caught, as if it hadn't been caught in the first place. 733 This becomes a problem combined with the stack unwinding used in termination 734 exception handling. 735 The stack is unwound before the handler is installed, and hence before any 736 reraises can run. So if a reraise happens the previous stack is gone, 737 the place on the stack where the default handler was supposed to run is gone, 738 if the default handler was a local function it may have been unwound too. 739 There is no reasonable way to restore that information, so the reraise has 740 to be considered as a new raise. 741 This is the strongest advantage conditional catches have over reraising, 742 they happen before stack unwinding and avoid this problem. 743 744 % The one possible disadvantage of conditional catch is that it runs user 745 % code during the exception search. While this is a new place that user code 746 % can be run destructors and finally clauses are already run during the stack 747 % unwinding. 774 (There is a simpler solution if @handle_a@ never raises exceptions, 775 using nested try statements.) 776 777 % } catch (an_exception * e ; check_a(e)) { 778 % handle_a(e); 779 % } catch (exception_t * e ; !(virtual an_exception *)e && check_b(e)) { 780 % handle_b(e); 781 % } 748 782 % 749 % https://www.cplusplus.com/reference/exception/current_exception/ 750 % `exception_ptr current_exception() noexcept;` 751 % https://www.python.org/dev/peps/pep-0343/ 752 \end{comment} 783 % } catch (an_exception * e) 784 % if (check_a(e)) { 785 % handle_a(e); 786 % } else throw; 787 % } catch (exception_t * e) 788 % if (check_b(e)) { 789 % handle_b(e); 790 % } else throw; 791 % } 792 In similar simple examples, translating from re-raise to conditional catch 793 takes less code but it does not have a general, trivial solution either. 794 795 So, given that the two patterns do not trivially translate into each other, 796 it becomes a matter of which on should be encouraged and made the default. 797 From the premise that if a handler could handle an exception then it 798 should, it follows that checking as many handlers as possible is preferred. 799 So, conditional catch and checking later handlers is a good default. 753 800 754 801 \section{Finally Clauses} 755 802 \label{s:FinallyClauses} 756 Finally clauses are used to p reform unconditional clean-up when leaving a803 Finally clauses are used to perform unconditional cleanup when leaving a 757 804 scope and are placed at the end of a try statement after any handler clauses: 758 805 \begin{cfa} … … 766 813 The @FINALLY_BLOCK@ is executed when the try statement is removed from the 767 814 stack, including when the @GUARDED_BLOCK@ finishes, any termination handler 768 finishes ,or during an unwind.815 finishes or during an unwind. 769 816 The only time the block is not executed is if the program is exited before 770 817 the stack is unwound. … … 772 819 Execution of the finally block should always finish, meaning control runs off 773 820 the end of the block. This requirement ensures control always continues as if 774 the finally clause is not present, \ie finally is for cleanup not changing821 the finally clause is not present, \ie finally is for cleanup, not changing 775 822 control flow. 776 823 Because of this requirement, local control flow out of the finally block 777 824 is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or 778 825 @return@ that causes control to leave the finally block. Other ways to leave 779 the finally block, such as a long jumpor termination are much harder to check,780 and at best requir ingadditional run-time overhead, and so are only826 the finally block, such as a @longjmp@ or termination are much harder to check, 827 and at best require additional run-time overhead, and so are only 781 828 discouraged. 782 829 783 Not all languages with unwinding have finally clauses. Notably \Cpp does830 Not all languages with unwinding have finally clauses. Notably, \Cpp does 784 831 without it as destructors, and the RAII design pattern, serve a similar role. 785 832 Although destructors and finally clauses can be used for the same cases, 786 833 they have their own strengths, similar to top-level function and lambda 787 834 functions with closures. 788 Destructors take more work for their creation, but if there is clean-up code835 Destructors take more work to create, but if there is clean-up code 789 836 that needs to be run every time a type is used, they are much easier 790 to set -up.791 On the other hand finally clauses capture the local context, so iseasy to792 use when the clean -up is not dependent on the type of a variable or requires837 to set up for each use. % It's automatic. 838 On the other hand, finally clauses capture the local context, so are easy to 839 use when the cleanup is not dependent on the type of a variable or requires 793 840 information from multiple variables. 794 841 … … 797 844 Cancellation is a stack-level abort, which can be thought of as as an 798 845 uncatchable termination. It unwinds the entire current stack, and if 799 possible forwards the cancellation exception to a different stack.846 possible, forwards the cancellation exception to a different stack. 800 847 801 848 Cancellation is not an exception operation like termination or resumption. 802 849 There is no special statement for starting a cancellation; instead the standard 803 library function @cancel_stack@ is called passing an exception. Unlike a804 raise, this exception is not used in matching only to pass information about850 library function @cancel_stack@ is called, passing an exception. Unlike a 851 raise, this exception is not used in matching, only to pass information about 805 852 the cause of the cancellation. 806 Final y, since a cancellation only unwinds and forwards, there is no default handler.807 808 After @cancel_stack@ is called the exception is copied into the EHM's memory853 Finally, as no handler is provided, there is no default handler. 854 855 After @cancel_stack@ is called, the exception is copied into the EHM's memory 809 856 and the current stack is unwound. 810 857 The behaviour after that depends on the kind of stack being cancelled. 811 858 812 859 \paragraph{Main Stack} 813 The main stack is the one used by the program main at the start of execution, 860 The main stack is the one used by 861 the program's main function at the start of execution, 814 862 and is the only stack in a sequential program. 815 After the main stack is unwound there is a program-level abort.816 817 The reasons for this semantics in a sequential program is that there is no more code to execute.818 This semantics also applies to concurrent programs, too, even if threads are running.819 That is, if any threads starts a cancellation, it implies all threads terminate. 820 Keeping the same behaviour in sequential and concurrent programs is simple.821 Also, even in concurrent programs there may not currently be any other stacks 822 and even if other stacks do exist, main has no way to know where they are.863 After the main stack is unwound, there is a program-level abort. 864 865 The first reason for this behaviour is for sequential programs where there 866 is only one stack, and hence no stack to pass information to. 867 Second, even in concurrent programs, the main stack has no dependency 868 on another stack and no reliable way to find another living stack. 869 Finally, keeping the same behaviour in both sequential and concurrent 870 programs is simple and easy to understand. 823 871 824 872 \paragraph{Thread Stack} … … 832 880 and an implicit join (from a destructor call). The explicit join takes the 833 881 default handler (@defaultResumptionHandler@) from its calling context while 834 the implicit join provides its own ;which does a program abort if the882 the implicit join provides its own, which does a program abort if the 835 883 @ThreadCancelled@ exception cannot be handled. 836 884 … … 850 898 851 899 With explicit join and a default handler that triggers a cancellation, it is 852 possible to cascade an error across any number of threads, cleaning up each 900 possible to cascade an error across any number of threads, 901 alternating between the resumption (possibly termination) and cancellation, 902 cleaning up each 853 903 in turn, until the error is handled or the main thread is reached. 854 904 … … 858 908 After a coroutine stack is unwound, control returns to the @resume@ function 859 909 that most recently resumed it. @resume@ reports a 860 @CoroutineCancelled@ exception, which contains a reference sto the cancelled910 @CoroutineCancelled@ exception, which contains a reference to the cancelled 861 911 coroutine and the exception used to cancel it. 862 912 The @resume@ function also takes the \defaultResumptionHandler{} from the 863 913 caller's context and passes it to the internal report. 864 914 865 A coroutine only knows of two other coroutines, its starter and its last resumer. 915 A coroutine only knows of two other coroutines, 916 its starter and its last resumer. 866 917 The starter has a much more distant connection, while the last resumer just 867 918 (in terms of coroutine state) called resume on this coroutine, so the message … … 869 920 870 921 With a default handler that triggers a cancellation, it is possible to 871 cascade an error across any number of coroutines, cleaning up each in turn, 922 cascade an error across any number of coroutines, 923 alternating between the resumption (possibly termination) and cancellation, 924 cleaning up each in turn, 872 925 until the error is handled or a thread stack is reached. 873 874 \PAB{Part of this I do not understand. A cancellation cannot be caught. But you875 talk about handling a cancellation in the last sentence. Which is correct?} -
doc/theses/andrew_beach_MMath/future.tex
rf95634e rb7fd9daf 2 2 \label{c:future} 3 3 4 The following discussion covers both possible interesting research 5 that could follow from this work as well as simple implementation 6 improvements. 7 4 8 \section{Language Improvements} 5 \todo{Future/Language Improvements seems to have gotten mixed up. It is 6 presented as ``waiting on language improvements" but really its more 7 non-research based impovements.} 9 8 10 \CFA is a developing programming language. As such, there are partially or 9 unimplemented features of the language (including several broken components) 10 that I had to workaround while building an exception handling system largely in 11 the \CFA language (some C components). The following are a few of these 12 issues, and once implemented/fixed, how they would affect the exception system. 11 unimplemented features (including several broken components) 12 that I had to work around while building the EHM largely in 13 the \CFA language (some C components). Below are a few of these issues 14 and how implementing/fixing them would affect the EHM. 15 In addition, there are some simple improvements that had no interesting 16 research attached to them but would make using the language easier. 13 17 \begin{itemize} 14 \item15 The implementation of termination is not portable because it includes16 hand-crafted assembly statements.17 The existing compilers cannot translate that for other platforms and those18 sections must be ported by hand to19 support more hardware architectures, such as the ARM processor.20 18 \item 21 19 Due to a type-system problem, the catch clause cannot bind the exception to a … … 24 22 result in little or no change in the exception system but simplify usage. 25 23 \item 24 The @copy@ function in the exception virtual table is an adapter to address 25 some limitations in the \CFA copy constructor. If the copy constructor is 26 improved it can be used directly without the adapter. 27 \item 26 28 Termination handlers cannot use local control-flow transfers, \eg by @break@, 27 29 @return@, \etc. The reason is that current code generation hoists a handler 28 into a nested function for convenience (versus assemble-code generation at the 29 @try@ statement). Hence, when the handler runs, its code is not in the lexical 30 scope of the @try@ statement, where the local control-flow transfers are 31 meaningful. 30 into a nested function for convenience (versus assembly-code generation at the 31 try statement). Hence, when the handler runs, it can still access local 32 variables in the lexical scope of the try statement. Still, it does mean 33 that seemingly local control flow is not in fact local and crosses a function 34 boundary. 35 Making the termination handler's code within the surrounding 36 function would remove this limitation. 37 % Try blocks are much more difficult to do practically (requires our own 38 % assembly) and resumption handlers have some theoretical complexity. 32 39 \item 33 There is no detection of colliding unwinds. It is possible for clean -up code34 run during an unwind to trigger another unwind that escapes the clean -up code35 itself ;such as a termination exception caught further down the stack or a36 cancellation. There do exist ways to handle this but currently they are not37 even detectedand the first unwind will simply be forgotten, often leaving40 There is no detection of colliding unwinds. It is possible for cleanup code 41 run during an unwind to trigger another unwind that escapes the cleanup code 42 itself, such as a termination exception caught further down the stack or a 43 cancellation. There do exist ways to handle this case, but currently there is 44 no detection and the first unwind will simply be forgotten, often leaving 38 45 it in a bad state. 39 46 \item 40 Also the exception system did not have a lot of time to be tried and tested.41 So just letting people use the exception system more will reveal new42 quality of life upgrades that can be made with time.47 Finally, the exception system has not had a lot of programmer testing. 48 More time with encouraged usage will reveal new 49 quality of life upgrades that can be made. 43 50 \end{itemize} 44 51 … … 47 54 project, but was thrust upon it to do exception inheritance; hence, only 48 55 minimal work is done. A draft for a complete virtual system is available but 49 it is not finalized.A future \CFA project is to complete that work and then56 not finalized. A future \CFA project is to complete that work and then 50 57 update the exception system that uses the current version. 51 58 … … 53 60 exception traits. The most important one is an assertion to check one virtual 54 61 type is a child of another. This check precisely captures many of the 55 correctness requirements. 62 current ad-hoc correctness requirements. 63 64 Other features of the virtual system could also remove some of the 65 special cases around exception virtual tables, such as the generation 66 of the @msg@ function. 56 67 57 68 The full virtual system might also include other improvement like associated 58 69 types to allow traits to refer to types not listed in their header. This 59 70 feature allows exception traits to not refer to the virtual-table type 60 explicitly, removing the need for the current interface macros. 71 explicitly, removing the need for the current interface macros, 72 such as @EHM_IS_EXCEPTION@. 61 73 62 74 \section{Additional Raises} 63 75 Several other kinds of exception raises were considered beyond termination 64 (@throw@), resumption (@throwResume@), and re raise.76 (@throw@), resumption (@throwResume@), and re-raise. 65 77 66 78 The first is a non-local/concurrent raise providing asynchronous exceptions, … … 74 86 Non-local/concurrent raise requires more 75 87 coordination between the concurrency system 76 and the exception system. Many of the interesting design decisions cent re88 and the exception system. Many of the interesting design decisions center 77 89 around masking, \ie controlling which exceptions may be thrown at a stack. It 78 90 would likely require more of the virtual system and would also effect how … … 93 105 Checked exceptions make exceptions part of a function's type by adding an 94 106 exception signature. An exception signature must declare all checked 95 exceptions that could propagate from the function (either because they were96 raised inside the function or came from a sub-function ). This improves safety107 exceptions that could propagate from the function, either because they were 108 raised inside the function or came from a sub-function. This improves safety 97 109 by making sure every checked exception is either handled or consciously 98 110 passed on. 99 111 100 However checked exceptions were never seriously considered for this project101 because they have significant trade-offs in usab lity and code reuse in112 Checked exceptions were never seriously considered for this project 113 because they have significant trade-offs in usability and code reuse in 102 114 exchange for the increased safety. 103 115 These trade-offs are most problematic when trying to pass exceptions through 104 116 higher-order functions from the functions the user passed into the 105 117 higher-order function. There are no well known solutions to this problem 106 that were satisfactory for \CFA (which carries some of C's flexibility107 oversafety design) so additional research is needed.118 that were satisfactory for \CFA (which carries some of C's 119 flexibility-over-safety design) so additional research is needed. 108 120 109 121 Follow-up work might add some form of checked exceptions to \CFA, … … 128 140 Zero-cost resumptions is still an open problem. First, because libunwind does 129 141 not support a successful-exiting stack-search without doing an unwind. 130 Workarounds are possible but awkward. Ideally an extension to libunwind could131 be made, but that would either require separate maintenance or gain enough132 support to have it folded into the standard.142 Workarounds are possible but awkward. Ideally, an extension to libunwind could 143 be made, but that would either require separate maintenance or gaining enough 144 support to have it folded into the official library itself. 133 145 134 Also new techniques to skip previously searched parts of the stack need to be146 Also, new techniques to skip previously searched parts of the stack need to be 135 147 developed to handle the recursive resume problem and support advanced algebraic 136 148 effects. … … 158 170 to leave the handler. 159 171 Currently, mimicking this behaviour in \CFA is possible by throwing a 160 termination inside a resumption handler.172 termination exception inside a resumption handler. 161 173 162 174 % Maybe talk about the escape; and escape CONTROL_STMT; statements or how -
doc/theses/andrew_beach_MMath/implement.tex
rf95634e rb7fd9daf 14 14 \label{s:VirtualSystem} 15 15 % Virtual table rules. Virtual tables, the pointer to them and the cast. 16 While the \CFA virtual system currently has only one public feature, virtual17 cast (see the virtual cast feature \vpageref{p:VirtualCast}),18 substantial structure is required to support it,16 While the \CFA virtual system currently has only two public features, virtual 17 cast and virtual tables, 18 substantial structure is required to support them, 19 19 and provide features for exception handling and the standard library. 20 20 21 21 \subsection{Virtual Type} 22 Virtual types only have one change to their structure: the addition of a 23 pointer to the virtual table, which is called the \emph{virtual-table pointer}. 24 Internally, the field is called \snake{virtual_table}. 25 The field is fixed after construction. It is always the first field in the 22 A virtual type~(see \autoref{s:virtuals}) has a pointer to a virtual table, 23 called the \emph{virtual-table pointer}, 24 which binds each instance of a virtual type to a virtual table. 25 Internally, the field is called \snake{virtual_table} 26 and is fixed after construction. 27 This pointer is also the table's id and how the system accesses the 28 virtual table and the virtual members there. 29 It is always the first field in the 26 30 structure so that its location is always known. 27 \todo{Talk about constructors for virtual types (after they are working).} 28 29 The virtual table pointer binds an instance of a virtual type 30 to a virtual table. 31 The pointer is also the table's id and how the system accesses the 32 virtual table and the virtual members there. 33 34 \subsection{Type Id} 35 Every virtual type has a unique id. 36 Type ids can be compared for equality, 37 which checks if the types reperented are the same, 38 or used to access the type's type information. 39 The type information currently is only the parent's type id or, if the 31 32 % We have no special rules for these constructors. 33 Virtual table pointers are passed to the constructors of virtual types 34 as part of field-by-field construction. 35 36 \subsection{Type ID} 37 Every virtual type has a unique ID. 38 These are used in type equality, to check if the representation of two values 39 are the same, and to access the type's type information. 40 This uniqueness means across a program composed of multiple translation 41 units (TU), not uniqueness across all programs or even across multiple 42 processes on the same machine. 43 44 Our approach for program uniqueness is using a static declaration for each 45 type ID, where the run-time storage address of that variable is guaranteed to 46 be unique during program execution. 47 The type ID storage can also be used for other purposes, 48 and is used for type information. 49 50 The problem is that a type ID may appear in multiple TUs that compose a 51 program (see \autoref{ss:VirtualTable}), so the initial solution would seem 52 to be make it external in each translation unit. However, the type ID must 53 have a declaration in (exactly) one of the TUs to create the storage. 54 No other declaration related to the virtual type has this property, so doing 55 this through standard C declarations would require the user to do it manually. 56 57 Instead, the linker is used to handle this problem. 58 % I did not base anything off of C++17; they are solving the same problem. 59 A new feature has been added to \CFA for this purpose, the special attribute 60 \snake{cfa_linkonce}, which uses the special section @.gnu.linkonce@. 61 When used as a prefix (\eg @.gnu.linkonce.example@), the linker does 62 not combine these sections, but instead discards all but one with the same 63 full name. 64 65 So, each type ID must be given a unique section name with the \snake{linkonce} 66 prefix. Luckily, \CFA already has a way to get unique names, the name mangler. 67 For example, this could be written directly in \CFA: 68 \begin{cfa} 69 __attribute__((cfa_linkonce)) void f() {} 70 \end{cfa} 71 This is translated to: 72 \begin{cfa} 73 __attribute__((section(".gnu.linkonce._X1fFv___1"))) void _X1fFv___1() {} 74 \end{cfa} 75 This is done internally to access the name mangler. 76 This attribute is useful for other purposes, any other place a unique 77 instance required, and should eventually be made part of a public and 78 stable feature in \CFA. 79 80 \subsection{Type Information} 81 82 There is data stored at the type ID's declaration, the type information. 83 The type information currently is only the parent's type ID or, if the 40 84 type has no parent, the null pointer. 41 42 The id's are implemented as pointers to the type's type information instance. 43 Dereferencing the pointer gets the type information. 44 The ancestors of a virtual type are found by traversing type ids through 85 The ancestors of a virtual type are found by traversing type IDs through 45 86 the type information. 46 The information pushes the issue of creating a unique value (for 47 the type id) to the problem of creating a unique instance (for type 48 information), which the linker can solve. 49 50 The advanced linker support is used here to avoid having to create 51 a new declaration to attach this data to. 52 With C/\CFA's header/implementation file divide for something to appear 53 exactly once it must come from a declaration that appears in exactly one 54 implementation file; the declarations in header files may exist only once 55 they can be included in many different translation units. 56 Therefore, structure's declaration will not work. 57 Neither will attaching the type information to the virtual table -- although 58 a vtable declarations are in implemention files they are not unique, see 59 \autoref{ss:VirtualTable}. 60 Instead the same type information is generated multiple times and then 61 the new attribute \snake{cfa_linkone} is used to removed duplicates. 87 An example using helper macros looks like: 88 \begin{cfa} 89 struct INFO_TYPE(TYPE) { 90 INFO_TYPE(PARENT) const * parent; 91 }; 92 93 __attribute__((cfa_linkonce)) 94 INFO_TYPE(TYPE) const INFO_NAME(TYPE) = { 95 &INFO_NAME(PARENT), 96 }; 97 \end{cfa} 62 98 63 99 Type information is constructed as follows: 64 \begin{enumerate} 100 \begin{enumerate}[nosep] 65 101 \item 66 Use the type's name to generate a name for the type information structure .67 This is saved so it maybe reused.102 Use the type's name to generate a name for the type information structure, 103 which is saved so it can be reused. 68 104 \item 69 105 Generate a new structure definition to store the type 70 information. The layout is the same in each case, just the parent's type id,106 information. The layout is the same in each case, just the parent's type ID, 71 107 but the types used change from instance to instance. 72 The generated name is used for both this structure and, if rel ivant, the108 The generated name is used for both this structure and, if relevant, the 73 109 parent pointer. 74 110 If the virtual type is polymorphic then the type information structure is 75 111 polymorphic as well, with the same polymorphic arguments. 76 112 \item 77 A sep erate name for instances is generated from the type's name.113 A separate name for instances is generated from the type's name. 78 114 \item 79 The definition is generated and initiali sed.80 The parent idis set to the null pointer or to the address of the parent's115 The definition is generated and initialized. 116 The parent ID is set to the null pointer or to the address of the parent's 81 117 type information instance. Name resolution handles the rest. 82 118 \item 83 119 \CFA's name mangler does its regular name mangling encoding the type of 84 the declaration into the instance name. This gives a completely unique name 120 the declaration into the instance name. 121 This process gives a completely unique name 85 122 including different instances of the same polymorphic type. 86 123 \end{enumerate} 87 \todo{The list is making me realise, some of this isn't ordered.}88 124 89 125 Writing that code manually, with helper macros for the early name mangling, … … 100 136 \end{cfa} 101 137 138 \begin{comment} 102 139 \subsubsection{\lstinline{cfa\_linkonce} Attribute} 103 % I just reali sed: This is an extension of the inline keyword.140 % I just realized: This is an extension of the inline keyword. 104 141 % An extension of C's at least, it is very similar to C++'s. 105 142 Another feature added to \CFA is a new attribute: \texttt{cfa\_linkonce}. … … 113 150 file as if it was a forward declaration, except no definition is required. 114 151 115 This technique is used for type -idinstances. A link-once definition is152 This technique is used for type ID instances. A link-once definition is 116 153 generated each time the structure is seen. This will result in multiple 117 154 copies but the link-once attribute ensures all but one are removed for a … … 126 163 everything that comes after the special prefix, then only one is used 127 164 and the other is discarded. 165 \end{comment} 128 166 129 167 \subsection{Virtual Table} 130 168 \label{ss:VirtualTable} 131 Each virtual type has a virtual table type that stores its type idand169 Each virtual type has a virtual table type that stores its type ID and 132 170 virtual members. 133 Each virtual type instance is bound to a table instance that is filled with 134 the values of virtual members. 135 Both the layout of the fields and their value are decided by the rules given 171 An instance of a virtual type is bound to a virtual table instance, 172 which have the values of the virtual members. 173 Both the layout of the fields (in the virtual table type) 174 and their value (in the virtual table instance) are decided by the rules given 136 175 below. 137 176 138 The layout always comes in three parts. 139 \todo{Add labels to the virtual table layout figure.} 140 The first section is just the type id at the head of the table. It is always 177 The layout always comes in three parts (see \autoref{f:VirtualTableLayout}). 178 The first section is just the type ID at the head of the table. It is always 141 179 there to ensure that it can be found even when the accessing code does not 142 180 know which virtual type it has. 143 The second section areall the virtual members of the parent, in the same181 The second section is all the virtual members of the parent, in the same 144 182 order as they appear in the parent's virtual table. Note that the type may 145 change slightly as references to the ``this" willchange. This is limited to183 change slightly as references to the ``this" change. This is limited to 146 184 inside pointers/references and via function pointers so that the size (and 147 185 hence the offsets) are the same. … … 150 188 151 189 \begin{figure} 190 \begin{center} 152 191 \input{vtable-layout} 192 \end{center} 153 193 \caption{Virtual Table Layout} 154 194 \label{f:VirtualTableLayout} 155 \todo*{Improve the Virtual Table Layout diagram.}156 195 \end{figure} 157 196 … … 160 199 This, combined with the fixed offset to the virtual table pointer, means that 161 200 for any virtual type, it is always safe to access its virtual table and, 162 from there, it is safe to check the type idto identify the exact type of the201 from there, it is safe to check the type ID to identify the exact type of the 163 202 underlying object, access any of the virtual members and pass the object to 164 203 any of the method-like virtual members. … … 168 207 the context of the declaration. 169 208 170 The type id is always fixed;with each virtual table type having171 exactly one possible type id.209 The type ID is always fixed, with each virtual table type having 210 exactly one possible type ID. 172 211 The virtual members are usually filled in by type resolution. 173 212 The best match for a given name and type at the declaration site is used. 174 213 There are two exceptions to that rule: the @size@ field, the type's size, 175 is set using a @sizeof@ expression and the @align@ field, the214 is set using a @sizeof@ expression, and the @align@ field, the 176 215 type's alignment, is set using an @alignof@ expression. 177 216 178 \subsubsection{Concurrency Integration} 217 Most of these tools are already inside the compiler. Using simple 218 code transformations early on in compilation allows most of that work to be 219 handed off to the existing tools. \autoref{f:VirtualTableTransformation} 220 shows an example transformation; this example shows an exception virtual table. 221 It also shows the transformation on the full declaration. 222 For a forward declaration, the @extern@ keyword is preserved and the 223 initializer is not added. 224 225 \begin{figure}[htb] 226 \begin{cfa} 227 vtable(example_type) example_name; 228 \end{cfa} 229 \transformline 230 % Check mangling. 231 \begin{cfa} 232 const struct example_type_vtable example_name = { 233 .__cfavir_typeid : &__cfatid_example_type, 234 .size : sizeof(example_type), 235 .copy : copy, 236 .^?{} : ^?{}, 237 .msg : msg, 238 }; 239 \end{cfa} 240 \caption{Virtual Table Transformation} 241 \label{f:VirtualTableTransformation} 242 \end{figure} 243 244 \subsection{Concurrency Integration} 179 245 Coroutines and threads need instances of @CoroutineCancelled@ and 180 246 @ThreadCancelled@ respectively to use all of their functionality. When a new … … 183 249 at the definition of the main function. 184 250 185 This is showned through code re-writing in 186 \autoref{f:ConcurrencyTypeTransformation} and 187 \autoref{f:ConcurrencyMainTransformation}. 188 In both cases the original declaration is not modified, 251 These transformations are shown through code re-writing in 252 \autoref{f:CoroutineTypeTransformation} and 253 \autoref{f:CoroutineMainTransformation}. 254 Threads use the same pattern, with some names and types changed. 255 In both cases, the original declaration is not modified, 189 256 only new ones are added. 190 257 191 \begin{figure} 258 \begin{figure}[htb] 192 259 \begin{cfa} 193 260 coroutine Example { … … 207 274 extern CoroutineCancelled_vtable & _default_vtable; 208 275 \end{cfa} 209 \caption{Co ncurrencyType Transformation}210 \label{f:Co ncurrencyTypeTransformation}276 \caption{Coroutine Type Transformation} 277 \label{f:CoroutineTypeTransformation} 211 278 \end{figure} 212 279 213 \begin{figure} 280 \begin{figure}[htb] 214 281 \begin{cfa} 215 282 void main(Example & this) { … … 229 296 &_default_vtable_object_declaration; 230 297 \end{cfa} 231 \caption{Co ncurrencyMain Transformation}232 \label{f:Co ncurrencyMainTransformation}298 \caption{Coroutine Main Transformation} 299 \label{f:CoroutineMainTransformation} 233 300 \end{figure} 234 301 … … 242 309 \begin{cfa} 243 310 void * __cfa__virtual_cast( 244 struct __cfavir_type_td parent, 245 struct __cfavir_type_id const * child ); 246 \end{cfa} 247 The type id of target type of the virtual cast is passed in as @parent@ and 311 struct __cfavir_type_id * parent, 312 struct __cfavir_type_id * const * child ); 313 \end{cfa} 314 The type ID for the target type of the virtual cast is passed in as 315 @parent@ and 248 316 the cast target is passed in as @child@. 249 250 For generated C code wraps both arguments and the result with type casts. 317 The generated C code wraps both arguments and the result with type casts. 251 318 There is also an internal check inside the compiler to make sure that the 252 319 target type is a virtual type. … … 255 322 The virtual cast either returns the original pointer or the null pointer 256 323 as the new type. 257 So the function does the parent check and returns the appropriate value.258 The parent check is a simple linear search of child's ancestors using the324 The function does the parent check and returns the appropriate value. 325 The parent check is a simple linear search of the child's ancestors using the 259 326 type information. 260 327 261 328 \section{Exceptions} 262 % Anything about exception construction. 329 % The implementation of exception types. 330 331 Creating exceptions can be roughly divided into two parts: 332 the exceptions themselves and the virtual system interactions. 333 334 Creating an exception type is just a matter of prepending the field 335 with the virtual table pointer to the list of the fields 336 (see \autoref{f:ExceptionTypeTransformation}). 337 338 \begin{figure}[htb] 339 \begin{cfa} 340 exception new_exception { 341 // EXISTING FIELDS 342 }; 343 \end{cfa} 344 \transformline 345 \begin{cfa} 346 struct new_exception { 347 struct new_exception_vtable const * virtual_table; 348 // EXISTING FIELDS 349 }; 350 \end{cfa} 351 \caption{Exception Type Transformation} 352 \label{f:ExceptionTypeTransformation} 353 \end{figure} 354 355 The integration between exceptions and the virtual system is a bit more 356 complex simply because of the nature of the virtual system prototype. 357 The primary issue is that the virtual system has no way to detect when it 358 should generate any of its internal types and data. This is handled by 359 the exception code, which tells the virtual system when to generate 360 its components. 361 362 All types associated with a virtual type, 363 the types of the virtual table and the type ID, 364 are generated when the virtual type (the exception) is first found. 365 The type ID (the instance) is generated with the exception, if it is 366 a monomorphic type. 367 However, if the exception is polymorphic, then a different type ID has to 368 be generated for every instance. In this case, generation is delayed 369 until a virtual table is created. 370 % There are actually some problems with this, which is why it is not used 371 % for monomorphic types. 372 When a virtual table is created and initialized, two functions are created 373 to fill in the list of virtual members. 374 The first is the @copy@ function that adapts the exception's copy constructor 375 to work with pointers, avoiding some issues with the current copy constructor 376 interface. 377 Second is the @msg@ function that returns a C-string with the type's name, 378 including any polymorphic parameters. 263 379 264 380 \section{Unwinding} … … 274 390 stack. On function entry and return, unwinding is handled directly by the 275 391 call/return code embedded in the function. 276 In many cases, the position of the instruction pointer (relative to parameter 277 and local declarations) is enough to know the current size of the stack 278 frame. 279 392 393 % Discussing normal stack unwinding: 280 394 Usually, the stack-frame size is known statically based on parameter and 281 local variable declarations. Even withdynamic stack-size, the information395 local variable declarations. Even for a dynamic stack-size, the information 282 396 to determine how much of the stack has to be removed is still contained 283 397 within the function. … … 285 399 bumping the hardware stack-pointer up or down as needed. 286 400 Constructing/destructing values within a stack frame has 287 a similar complexity but can add additional work and take longer. 288 289 Unwinding across multiple stack frames is more complex because that 401 a similar complexity but larger constants. 402 403 % Discussing multiple frame stack unwinding: 404 Unwinding across multiple stack frames is more complex, because that 290 405 information is no longer contained within the current function. 291 With seperate compilation a function has no way of knowing what its callers 292 are so it can't know how large those frames are. 293 Without altering the main code path it is also hard to pass that work off 294 to the caller. 295 296 The traditional unwinding mechanism for C is implemented by saving a snap-shot 297 of a function's state with @setjmp@ and restoring that snap-shot with 406 With separate compilation, 407 a function does not know its callers nor their frame layout. 408 Even using the return address, that information is encoded in terms of 409 actions in code, intermixed with the actions required to finish the function. 410 Without changing the main code path it is impossible to select one of those 411 two groups of actions at the return site. 412 413 The traditional unwinding mechanism for C is implemented by saving a snapshot 414 of a function's state with @setjmp@ and restoring that snapshot with 298 415 @longjmp@. This approach bypasses the need to know stack details by simply 299 reseting to a snap-shot of an arbitrary but existing function frame on the 300 stack. It is up to the programmer to ensure the snap-shot is valid when it is 301 reset and that all required clean-up from the unwound stacks is performed. 302 This approach is fragile and requires extra work in the surrounding code. 303 304 With respect to the extra work in the surounding code, 305 many languages define clean-up actions that must be taken when certain 306 sections of the stack are removed. Such as when the storage for a variable 307 is removed from the stack or when a try statement with a finally clause is 416 resetting to a snapshot of an arbitrary but existing function frame on the 417 stack. It is up to the programmer to ensure the snapshot is valid when it is 418 reset and that all required cleanup from the unwound stacks is performed. 419 Because it does not automate or check any of this cleanup, 420 it can be easy to make mistakes and always must be handled manually. 421 422 With respect to the extra work in the surrounding code, 423 many languages define cleanup actions that must be taken when certain 424 sections of the stack are removed, such as when the storage for a variable 425 is removed from the stack, possibly requiring a destructor call, 426 or when a try statement with a finally clause is 308 427 (conceptually) popped from the stack. 309 None of these should be handled by the user --- that would contradict the310 intention of these features -- -so they need to be handled automatically.428 None of these cases should be handled by the user -- that would contradict the 429 intention of these features -- so they need to be handled automatically. 311 430 312 431 To safely remove sections of the stack, the language must be able to find and 313 run these clean -up actions even when removing multiple functions unknown at432 run these cleanup actions even when removing multiple functions unknown at 314 433 the beginning of the unwinding. 315 434 … … 317 436 library that provides tools for stack walking, handler execution, and 318 437 unwinding. What follows is an overview of all the relevant features of 319 libunwind needed for this work, and how \CFA uses them to implement exception 320 handling. 438 libunwind needed for this work. 439 Following that is the description of the \CFA code that uses libunwind 440 to implement termination. 321 441 322 442 \subsection{libunwind Usage} … … 348 468 In plain C (which \CFA currently compiles down to) this 349 469 flag only handles the cleanup attribute: 470 %\label{code:cleanup} 350 471 \begin{cfa} 351 472 void clean_up( int * var ) { ... } … … 355 476 in this case @clean_up@, run when the variable goes out of scope. 356 477 This feature is enough to mimic destructors, 357 but not try statements which can effect478 but not try statements that affect 358 479 the unwinding. 359 480 360 481 To get full unwinding support, all of these features must be handled directly 361 in assembly and assembler directives; parti ularly the cfi directives482 in assembly and assembler directives; particularly the cfi directives 362 483 \snake{.cfi_lsda} and \snake{.cfi_personality}. 363 484 … … 399 520 @_UA_FORCE_UNWIND@ specifies a forced unwind call. Forced unwind only performs 400 521 the cleanup phase and uses a different means to decide when to stop 401 (see \ vref{s:ForcedUnwind}).522 (see \autoref{s:ForcedUnwind}). 402 523 \end{enumerate} 403 524 404 525 The @exception_class@ argument is a copy of the 405 526 \code{C}{exception}'s @exception_class@ field, 406 which is a number that identifies the exception handling mechanism527 which is a number that identifies the EHM 407 528 that created the exception. 408 529 … … 410 531 provided storage object. It has two public fields: the @exception_class@, 411 532 which is described above, and the @exception_cleanup@ function. 412 The clean -up function is used by the EHM to clean-up the exception, if it533 The cleanup function is used by the EHM to clean up the exception. If it 413 534 should need to be freed at an unusual time, it takes an argument that says 414 535 why it had to be cleaned up. … … 432 553 of the most recent stack frame. It continues to call personality functions 433 554 traversing the stack from newest to oldest until a function finds a handler or 434 the end of the stack is reached. In the latter case, raise exception returns435 @_U RC_END_OF_STACK@.436 437 Second, when a handler is matched, raise exception moves to the clean-up438 phase and walks the stack a second time.555 the end of the stack is reached. In the latter case, 556 @_Unwind_RaiseException@ returns @_URC_END_OF_STACK@. 557 558 Second, when a handler is matched, @_Unwind_RaiseException@ 559 moves to the cleanup phase and walks the stack a second time. 439 560 Once again, it calls the personality functions of each stack frame from newest 440 561 to oldest. This pass stops at the stack frame containing the matching handler. 441 If that personality function has not install a handler, it is an error.442 443 If an error is encountered, raise exceptionreturns either562 If that personality function has not installed a handler, it is an error. 563 564 If an error is encountered, @_Unwind_RaiseException@ returns either 444 565 @_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the 445 566 error occurred. … … 452 573 _Unwind_Stop_Fn, void *); 453 574 \end{cfa} 454 It also unwinds the stack but it does not use the search phase. Instead another 575 It also unwinds the stack but it does not use the search phase. Instead, 576 another 455 577 function, the stop function, is used to stop searching. The exception is the 456 same as the one passed to raise exception. The extra arguments are the stop 578 same as the one passed to @_Unwind_RaiseException@. 579 The extra arguments are the stop 457 580 function and the stop parameter. The stop function has a similar interface as a 458 581 personality function, except it is also passed the stop parameter. … … 494 617 needs its own exception context. 495 618 496 The exception context should be retrieved by calling the function619 The current exception context should be retrieved by calling the function 497 620 \snake{this_exception_context}. 498 621 For sequential execution, this function is defined as … … 519 642 The first step of a termination raise is to copy the exception into memory 520 643 managed by the exception system. Currently, the system uses @malloc@, rather 521 than reserved memory or the stack top. The exception handling mechanismmanages644 than reserved memory or the stack top. The EHM manages 522 645 memory for the exception as well as memory for libunwind and the system's own 523 646 per-exception storage. … … 554 677 \newsavebox{\stackBox} 555 678 \begin{lrbox}{\codeBox} 556 \begin{ lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]679 \begin{cfa} 557 680 unsigned num_exceptions = 0; 558 681 void throws() { … … 573 696 throws(); 574 697 } 575 \end{ lstlisting}698 \end{cfa} 576 699 \end{lrbox} 577 700 578 701 \begin{lrbox}{\stackBox} 579 702 \begin{lstlisting} 580 | try-finally581 | try -catch (Example)703 | finally block (Example) 704 | try block 582 705 throws() 583 | try-finally584 | try -catch (Example)706 | finally block (Example) 707 | try block 585 708 throws() 586 | try-finally587 | try -catch (Example)709 | finally block (Example) 710 | try block 588 711 throws() 589 712 main() … … 598 721 \label{f:MultipleExceptions} 599 722 \end{figure} 600 \todo*{Work on multiple exceptions code sample.}601 723 602 724 All exceptions are stored in nodes, which are then linked together in lists 603 725 one list per stack, with the 604 726 list head stored in the exception context. Within each linked list, the most 605 recently thrown exception is at the head followed by older thrown727 recently thrown exception is at the head, followed by older thrown 606 728 exceptions. This format allows exceptions to be thrown, while a different 607 729 exception is being handled. The exception at the head of the list is currently … … 614 736 exception into managed memory. After the exception is handled, the free 615 737 function is used to clean up the exception and then the entire node is 616 passed to free, returning the memory back to the heap.738 passed to @free@, returning the memory back to the heap. 617 739 618 740 \subsection{Try Statements and Catch Clauses} 619 741 The try statement with termination handlers is complex because it must 620 compensate for the C code-generation versus 742 compensate for the C code-generation versus proper 621 743 assembly-code generated from \CFA. Libunwind 622 744 requires an LSDA and personality function for control to unwind across a 623 745 function. The LSDA in particular is hard to mimic in generated C code. 624 746 625 The workaround is a function called @__cfaehm_try_terminate@ in the standard626 library. The contents of a try block and the termination handlers are converted 627 into functions. These are then passed to the try terminate function and it 628 calls them.747 The workaround is a function called \snake{__cfaehm_try_terminate} in the 748 standard \CFA library. The contents of a try block and the termination 749 handlers are converted into nested functions. These are then passed to the 750 try terminate function and it calls them, appropriately. 629 751 Because this function is known and fixed (and not an arbitrary function that 630 happens to contain a try statement), theLSDA can be generated ahead752 happens to contain a try statement), its LSDA can be generated ahead 631 753 of time. 632 754 633 Both the LSDA and the personality function are set ahead of time using 755 Both the LSDA and the personality function for \snake{__cfaehm_try_terminate} 756 are set ahead of time using 634 757 embedded assembly. This assembly code is handcrafted using C @asm@ statements 635 758 and contains 636 enough information for a single try statement the function repersents.759 enough information for the single try statement the function represents. 637 760 638 761 The three functions passed to try terminate are: 639 762 \begin{description} 640 \item[try function:] This function is the try block , it is where all the code763 \item[try function:] This function is the try block. It is where all the code 641 764 from inside the try block is placed. It takes no parameters and has no 642 765 return value. This function is called during regular execution to run the try … … 646 769 decides if a catch clause matches the termination exception. It is constructed 647 770 from the conditional part of each handler and runs each check, top to bottom, 648 in turn, first checking to see if the exception type matches and then if the 649 condition is true. It takes a pointer to the exception and returns 0 if the 650 exception is not handled here. Otherwise the return value is the id of the 771 in turn, to see if the exception matches this handler. 772 The match is performed in two steps: first, a virtual cast is used to check 773 if the raised exception is an instance of the declared exception type or 774 one of its descendant types, and then the condition is evaluated, if 775 present. 776 The match function takes a pointer to the exception and returns 0 if the 777 exception is not handled here. Otherwise, the return value is the ID of the 651 778 handler that matches the exception. 652 779 … … 659 786 \end{description} 660 787 All three functions are created with GCC nested functions. GCC nested functions 661 can be used to create closures, 662 in other words functions that can refer to the state of other 663 functions on the stack. This approach allows the functions to refer to all the 788 can be used to create closures; 789 in other words, 790 functions that can refer to variables in their lexical scope even though 791 those variables are part of a different function. 792 This approach allows the functions to refer to all the 664 793 variables in scope for the function containing the @try@ statement. These 665 794 nested functions and all other functions besides @__cfaehm_try_terminate@ in … … 669 798 670 799 \autoref{f:TerminationTransformation} shows the pattern used to transform 671 a \CFA try statement with catch clauses into the approprate C functions. 672 \todo{Explain the Termination Transformation figure.} 800 a \CFA try statement with catch clauses into the appropriate C functions. 673 801 674 802 \begin{figure} … … 728 856 \caption{Termination Transformation} 729 857 \label{f:TerminationTransformation} 730 \todo*{Improve (compress?) Termination Transformations.}731 858 \end{figure} 732 859 … … 738 865 Instead of storing the data in a special area using assembly, 739 866 there is just a linked list of possible handlers for each stack, 740 with each node on the list rep erenting a try statement on the stack.867 with each node on the list representing a try statement on the stack. 741 868 742 869 The head of the list is stored in the exception context. … … 744 871 to the head of the list. 745 872 Instead of traversing the stack, resumption handling traverses the list. 746 At each node, the EHM checks to see if the try statement the node rep ersents873 At each node, the EHM checks to see if the try statement the node represents 747 874 can handle the exception. If it can, then the exception is handled and 748 the operation finishes , otherwisethe search continues to the next node.875 the operation finishes; otherwise, the search continues to the next node. 749 876 If the search reaches the end of the list without finding a try statement 750 that can handle the exception, the default handler is executed and the 751 operation finishes. 877 with a handler clause 878 that can handle the exception, the default handler is executed. 879 If the default handler returns, control continues after the raise statement. 752 880 753 881 Each node has a handler function that does most of the work. 754 882 The handler function is passed the raised exception and returns true 755 883 if the exception is handled and false otherwise. 756 757 884 The handler function checks each of its internal handlers in order, 758 top-to-bottom, until it f unds a match. If a match is found that handler is885 top-to-bottom, until it finds a match. If a match is found that handler is 759 886 run, after which the function returns true, ignoring all remaining handlers. 760 887 If no match is found the function returns false. 761 The match is performed in two steps, first a virtual cast is used to see 762 if the thrown exception is an instance of the declared exception or one of 763 its descendant type, then check to see if passes the custom predicate if one 764 is defined. This ordering gives the type guarantee used in the predicate. 888 The match is performed in two steps. First a virtual cast is used to see 889 if the raised exception is an instance of the declared exception type or one 890 of its descendant types, if so, then the second step is to see if the 891 exception passes the custom predicate 892 if one is defined. 893 % You need to make sure the type is correct before running the predicate 894 % because the predicate can depend on that. 765 895 766 896 \autoref{f:ResumptionTransformation} shows the pattern used to transform 767 a \CFA try statement with catch clauses into the approprate C functions.768 \todo{Explain the Resumption Transformation figure.} 897 a \CFA try statement with catchResume clauses into the appropriate 898 C functions. 769 899 770 900 \begin{figure} … … 807 937 \caption{Resumption Transformation} 808 938 \label{f:ResumptionTransformation} 809 \todo*{Improve (compress?) Resumption Transformations.}810 939 \end{figure} 811 940 812 941 % Recursive Resumption Stuff: 813 942 \autoref{f:ResumptionMarking} shows search skipping 814 (see \ vpageref{s:ResumptionMarking}), which ignores parts of943 (see \autoref{s:ResumptionMarking}), which ignores parts of 815 944 the stack 816 already examined, is accomplished by updating the front of the list as the 817 search continues. Before the handler at a node is called, the head of the list 945 already examined, and is accomplished by updating the front of the list as 946 the search continues. 947 Before the handler is called at a matching node, the head of the list 818 948 is updated to the next node of the current node. After the search is complete, 819 949 successful or not, the head of the list is reset. … … 822 952 been checked are not on the list while a handler is run. If a resumption is 823 953 thrown during the handling of another resumption, the active handlers and all 824 the other handler checked up to this point are not checked again.954 the other handlers checked up to this point are not checked again. 825 955 % No paragraph? 826 956 This structure also supports new handlers added while the resumption is being 827 957 handled. These are added to the front of the list, pointing back along the 828 stack -- - the first one points over all the checked handlers ---958 stack -- the first one points over all the checked handlers -- 829 959 and the ordering is maintained. 830 960 831 961 \begin{figure} 962 \centering 832 963 \input{resumption-marking} 833 964 \caption{Resumption Marking} 834 965 \label{f:ResumptionMarking} 835 \todo*{Label Resumption Marking to aid clarity.}836 966 \end{figure} 837 967 … … 851 981 \section{Finally} 852 982 % Uses destructors and GCC nested functions. 853 A finally clause is placed into a GCC nested-function with a unique name, 854 and no arguments or return values. 855 This nested function is then set as the cleanup 856 function of an empty object that is declared at the beginning of a block placed 857 around the context of the associated @try@ statement. 858 859 The rest is handled by GCC. The try block and all handlers are inside this 860 block. At completion, control exits the block and the empty object is cleaned 983 984 %\autoref{code:cleanup} 985 A finally clause is handled by converting it into a once-off destructor. 986 The code inside the clause is placed into a GCC nested-function 987 with a unique name, and no arguments or return values. 988 This nested function is 989 then set as the cleanup function of an empty object that is declared at the 990 beginning of a block placed around the context of the associated try 991 statement, as shown in \autoref{f:FinallyTransformation}. 992 993 \begin{figure} 994 \begin{cfa} 995 try { 996 // TRY BLOCK 997 } finally { 998 // FINALLY BLOCK 999 } 1000 \end{cfa} 1001 1002 \transformline 1003 1004 \begin{cfa} 1005 { 1006 void finally(void *__hook){ 1007 // FINALLY BLOCK 1008 } 1009 __attribute__ ((cleanup(finally))) 1010 struct __cfaehm_cleanup_hook __finally_hook; 1011 { 1012 // TRY BLOCK 1013 } 1014 } 1015 \end{cfa} 1016 1017 \caption{Finally Transformation} 1018 \label{f:FinallyTransformation} 1019 \end{figure} 1020 1021 The rest is handled by GCC. 1022 The TRY BLOCK 1023 contains the try block itself as well as all code generated for handlers. 1024 Once that code has completed, 1025 control exits the block and the empty object is cleaned 861 1026 up, which runs the function that contains the finally code. 862 1027 … … 864 1029 % Stack selections, the three internal unwind functions. 865 1030 866 Cancellation also uses libunwind to do its stack traversal and unwinding ,867 howeverit uses a different primary function: @_Unwind_ForcedUnwind@. Details868 of its interface can be found in theSection~\vref{s:ForcedUnwind}.1031 Cancellation also uses libunwind to do its stack traversal and unwinding. 1032 However, it uses a different primary function: @_Unwind_ForcedUnwind@. Details 1033 of its interface can be found in Section~\vref{s:ForcedUnwind}. 869 1034 870 1035 The first step of cancellation is to find the cancelled stack and its type: … … 887 1052 passed to the forced-unwind function. The general pattern of all three stop 888 1053 functions is the same: continue unwinding until the end of stack and 889 then p reform the appropriate transfer.1054 then perform the appropriate transfer. 890 1055 891 1056 For main stack cancellation, the transfer is just a program abort. -
doc/theses/andrew_beach_MMath/intro.tex
rf95634e rb7fd9daf 11 11 12 12 % Now take a step back and explain what exceptions are generally. 13 Exception handling provides dynamic inter-function control flow. 13 14 A language's EHM is a combination of language syntax and run-time 14 components that are used to construct, raise, and handle exceptions, 15 including all control flow. 16 Exceptions are an active mechanism for replacing passive error/return codes and return unions (Go and Rust). 17 Exception handling provides dynamic inter-function control flow. 15 components that construct, raise, propagate and handle exceptions, 16 to provide all of that control flow. 18 17 There are two forms of exception handling covered in this thesis: 19 18 termination, which acts as a multi-level return, 20 19 and resumption, which is a dynamic function call. 21 % PAB: Maybe this sentence was suppose to be deleted? 22 Termination handling is much more common, 23 to the extent that it is often seen as the only form of handling. 24 % PAB: I like this sentence better than the next sentence. 25 % This separation is uncommon because termination exception handling is so 26 % much more common that it is often assumed. 27 % WHY: Mention other forms of continuation and \cite{CommonLisp} here? 28 29 Exception handling relies on the concept of nested functions to create handlers that deal with exceptions. 20 % About other works: 21 Often, when this separation is not made, termination exceptions are assumed 22 as they are more common and may be the only form of handling provided in 23 a language. 24 25 All types of exception handling link a raise with a handler. 26 Both operations are usually language primitives, although raises can be 27 treated as a function that takes an exception argument. 28 Handlers are more complex, as they are added to and removed from the stack 29 during execution, must specify what they can handle and must give the code to 30 handle the exception. 31 32 Exceptions work with different execution models but for the descriptions 33 that follow a simple call stack, with functions added and removed in a 34 first-in-last-out order, is assumed. 35 36 Termination exception handling searches the stack for the handler, then 37 unwinds the stack to where the handler was found before calling it. 38 The handler is run inside the function that defined it and when it finishes 39 it returns control to that function. 30 40 \begin{center} 31 \begin{tabular}[t]{ll} 32 \begin{lstlisting}[aboveskip=0pt,belowskip=0pt,language=CFA,{moredelim=**[is][\color{red}]{@}{@}}] 33 void f( void (*hp)() ) { 34 hp(); 35 } 36 void g( void (*hp)() ) { 37 f( hp ); 38 } 39 void h( int @i@, void (*hp)() ) { 40 void @handler@() { // nested 41 printf( "%d\n", @i@ ); 42 } 43 if ( i == 1 ) hp = handler; 44 if ( i > 0 ) h( i - 1, hp ); 45 else g( hp ); 46 } 47 h( 2, 0 ); 48 \end{lstlisting} 49 & 50 \raisebox{-0.5\totalheight}{\input{handler}} 51 \end{tabular} 41 %\input{termination} 42 % 43 %\medskip 44 \input{termhandle.pstex_t} 45 % I hate these diagrams, but I can't access xfig to fix them and they are 46 % better than the alternative. 52 47 \end{center} 53 The nested function @handler@ in the second stack frame is explicitly passed to function @f@. 54 When this handler is called in @f@, it uses the parameter @i@ in the second stack frame, which is accessible by an implicit lexical-link pointer. 55 Setting @hp@ in @h@ at different points in the recursion, results in invoking a different handler.56 Exception handling extends this idea by eliminating explicit handler passing, and instead, performing a stack search for a handler that matches some criteria (conditional dynamic call), and calls the handler at the top of the stack. 57 It is the runtime search $O(N)$ that differentiates an EHM call (raise) from normal dynamic call $O(1)$ via a function or virtual-member pointer.58 59 Termination exception handling searches the stack for a handler, unwinds the stack to the frame containing the matching handler, and calling the handler at the top of the stack.48 49 Resumption exception handling searches the stack for a handler and then calls 50 it without removing any other stack frames. 51 The handler is run on top of the existing stack, often as a new function or 52 closure capturing the context in which the handler was defined. 53 After the handler has finished running, it returns control to the function 54 that preformed the raise, usually starting after the raise. 60 55 \begin{center} 61 \input{termination} 56 %\input{resumption} 57 % 58 %\medskip 59 \input{resumhandle.pstex_t} 60 % The other one. 62 61 \end{center} 63 Note, since the handler can reference variables in @h@, @h@ must remain on the stack for the handler call.64 After the handler returns, control continues after the lexical location of the handler in @h@ (static return)~\cite[p.~108]{Tennent77}.65 Unwinding allows recover to any previous66 function on the stack, skipping any functions between it and the67 function containing the matching handler.68 69 Resumption exception handling searches the stack for a handler, does \emph{not} unwind the stack to the frame containing the matching handler, and calls the handler at the top of the stack.70 \begin{center}71 \input{resumption}72 \end{center}73 After the handler returns, control continues after the resume in @f@ (dynamic return).74 Not unwinding allows fix up of the problem in @f@ by any previous function on the stack, without disrupting the current set of stack frames.75 62 76 63 Although a powerful feature, exception handling tends to be complex to set up 77 and expensive to use 64 and expensive to use, 78 65 so it is often limited to unusual or ``exceptional" cases. 79 The classic example is error handling , where exceptions are used to80 remove error handling logic from the main execution path, while paying66 The classic example is error handling; exceptions can be used to 67 remove error handling logic from the main execution path, and pay 81 68 most of the cost only when the error actually occurs. 82 69 … … 85 72 The \CFA EHM implements all of the common exception features (or an 86 73 equivalent) found in most other EHMs and adds some features of its own. 87 The design of all the features had to be adapted to \CFA's feature set as74 The design of all the features had to be adapted to \CFA's feature set, as 88 75 some of the underlying tools used to implement and express exception handling 89 76 in other languages are absent in \CFA. 90 Still the resulting basicsyntax resembles that of other languages:91 \begin{ lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]92 @try@{77 Still, the resulting syntax resembles that of other languages: 78 \begin{cfa} 79 try { 93 80 ... 94 81 T * object = malloc(request_size); 95 82 if (!object) { 96 @throw@OutOfMemory{fixed_allocation, request_size};83 throw OutOfMemory{fixed_allocation, request_size}; 97 84 } 98 85 ... 99 } @catch@(OutOfMemory * error) {86 } catch (OutOfMemory * error) { 100 87 ... 101 88 } 102 \end{ lstlisting}89 \end{cfa} 103 90 % A note that yes, that was a very fast overview. 104 91 The design and implementation of all of \CFA's EHM's features are … … 107 94 108 95 % The current state of the project and what it contributes. 109 The majority of the \CFA EHM is implemented in \CFA, except for a small amount of assembler code. 110 In addition, 111 a suite of tests and performance benchmarks were created as part of this project. 112 The \CFA implementation techniques are generally applicable in other programming 96 All of these features have been implemented in \CFA, 97 covering both changes to the compiler and the run-time. 98 In addition, a suite of test cases and performance benchmarks were created 99 alongside the implementation. 100 The implementation techniques are generally applicable in other programming 113 101 languages and much of the design is as well. 114 Some parts of the EHM use features unique to \CFA, and hence, 115 are harder to replicate in other programming languages. 116 % Talk about other programming languages. 117 Three well known programming languages with EHMs, %/exception handling 118 C++, Java and Python are examined in the performance work. However, these languages focus on termination 119 exceptions, so there is no comparison with resumption. 102 Some parts of the EHM use other features unique to \CFA and would be 103 harder to replicate in other programming languages. 120 104 121 105 The contributions of this work are: 122 106 \begin{enumerate} 123 107 \item Designing \CFA's exception handling mechanism, adapting designs from 124 other programming languages ,and creating new features.125 \item Implementing stack unwinding forthe \CFA EHM, including updating126 the \CFA compiler and run-time environment to generate and execute the EHM code.108 other programming languages and creating new features. 109 \item Implementing stack unwinding and the \CFA EHM, including updating 110 the \CFA compiler and the run-time environment. 127 111 \item Designing and implementing a prototype virtual system. 128 112 % I think the virtual system and per-call site default handlers are the only 129 113 % "new" features, everything else is a matter of implementation. 130 \item Creating tests and performance benchmarks to compare with EHM's in other languages. 114 \item Creating tests to check the behaviour of the EHM. 115 \item Creating benchmarks to check the performance of the EHM, 116 as compared to other languages. 131 117 \end{enumerate} 132 118 133 %\todo{I can't figure out a good lead-in to the roadmap.} 134 The thesis is organization as follows.135 The next section and parts of \autoref{c:existing} cover existing EHMs.136 New \CFAEHM features are introduced in \autoref{c:features},119 The rest of this thesis is organized as follows. 120 The current state of exceptions is covered in \autoref{s:background}. 121 The existing state of \CFA is covered in \autoref{c:existing}. 122 New EHM features are introduced in \autoref{c:features}, 137 123 covering their usage and design. 138 124 That is followed by the implementation of these features in 139 125 \autoref{c:implement}. 140 Performance results are presented in \autoref{c:performance}. 141 Summing up and possibilities for extending this project are discussed in \autoref{c:future}. 126 Performance results are examined in \autoref{c:performance}. 127 Possibilities to extend this project are discussed in \autoref{c:future}. 128 Finally, the project is summarized in \autoref{c:conclusion}. 142 129 143 130 \section{Background} 144 131 \label{s:background} 145 132 146 Exception handling is a well examined areain programming languages,147 with papers on the subject dating back the 70s~\cite{Goodenough75}.133 Exception handling has been examined before in programming languages, 134 with papers on the subject dating back 70s.\cite{Goodenough75} 148 135 Early exceptions were often treated as signals, which carried no information 149 except their identity. Ada~\cite{Ada} still uses this system. 150 151 The modern flag-ship for termination exceptions is \Cpp, 136 except their identity. 137 Ada originally used this system\cite{Ada}, but now allows for a string 138 message as a payload\cite{Ada12}. 139 140 The modern flagship for termination exceptions -- if one exists -- is \Cpp, 152 141 which added them in its first major wave of non-object-orientated features 153 in 1990. 154 % https://en.cppreference.com/w/cpp/language/history 155 While many EHMs have special exception types, 156 \Cpp has the ability to use any type as an exception. 157 However, this generality is not particularly useful, and has been pushed aside for classes, with a convention ofinheriting from142 in 1990.\cite{CppHistory} 143 Many EHMs have special exception types, 144 however \Cpp has the ability to use any type as an exception. 145 These were found to be not very useful and have been pushed aside for classes 146 inheriting from 158 147 \code{C++}{std::exception}. 159 While \Cpp has a special catch-all syntax @catch(...)@, there is no way to discriminate its exception type, so nothing can 160 be done with the caught value because nothing is known about it. 161 Instead the base exception-type \code{C++}{std::exception} is defined with common functionality (such as 162 the ability to print a message when the exception is raised but not caught) and all 148 Although there is a special catch-all syntax (@catch(...)@), there are no 149 operations that can be performed on the caught value, not even type inspection. 150 Instead, the base exception-type \code{C++}{std::exception} defines common 151 functionality (such as 152 the ability to describe the reason the exception was raised) and all 163 153 exceptions have this functionality. 164 Having a root exception-type seems to be the standard now, as the guaranteed functionality is worth 165 any lost in flexibility from limiting exceptions types to classes. 166 167 Java~\cite{Java} was the next popular language to use exceptions. 168 Its exception system largely reflects that of \Cpp, except it requires 169 exceptions to be a subtype of \code{Java}{java.lang.Throwable} 154 That trade-off, restricting usable types to gain guaranteed functionality, 155 is almost universal now, as without some common functionality it is almost 156 impossible to actually handle any errors. 157 158 Java was the next popular language to use exceptions.\cite{Java8} 159 Its exception system largely reflects that of \Cpp, except that it requires 160 you throw a child type of \code{Java}{java.lang.Throwable} 170 161 and it uses checked exceptions. 171 Checked exceptions are part of a function's interface defining all exceptions it or its called functions raise. 172 Using this information, it is possible to statically verify if a handler exists for all raised exception, \ie no uncaught exceptions. 173 Making exception information explicit, improves clarity and 174 safety, but can slow down programming. 175 For example, programming complexity increases when dealing with high-order methods or an overly specified 176 throws clause. However some of the issues are more 177 programming annoyances, such as writing/updating many exception signatures after adding or remove calls. 178 Java programmers have developed multiple programming ``hacks'' to circumvent checked exceptions negating the robustness it is suppose to provide. 179 For example, the ``catch-and-ignore" pattern, where the handler is empty because the exception does not appear relevant to the programmer versus 180 repairing or recovering from the exception. 162 Checked exceptions are part of a function's interface, 163 the exception signature of the function. 164 Every exception that could be raised from a function, either directly or 165 because it is not handled from a called function, is given. 166 Using this information, it is possible to statically verify if any given 167 exception is handled, and guarantee that no exception will go unhandled. 168 Making exception information explicit improves clarity and safety, 169 but can slow down or restrict programming. 170 For example, programming high-order functions becomes much more complex 171 if the argument functions could raise exceptions. 172 However, as odd it may seem, the worst problems are rooted in the simple 173 inconvenience of writing and updating exception signatures. 174 This has caused Java programmers to develop multiple programming ``hacks'' 175 to circumvent checked exceptions, negating their advantages. 176 One particularly problematic example is the ``catch-and-ignore'' pattern, 177 where an empty handler is used to handle an exception without doing any 178 recovery or repair. In theory that could be good enough to properly handle 179 the exception, but more often is used to ignore an exception that the 180 programmer does not feel is worth the effort of handling, for instance if 181 they do not believe it will ever be raised. 182 If they are incorrect, the exception will be silenced, while in a similar 183 situation with unchecked exceptions the exception would at least activate 184 the language's unhandled exception code (usually, a program abort with an 185 error message). 181 186 182 187 %\subsection 183 188 Resumption exceptions are less popular, 184 although resumption is as old as termination; 185 hence, few 189 although resumption is as old as termination; that is, few 186 190 programming languages have implemented them. 187 191 % http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/parc/techReports/ 188 192 % CSL-79-3_Mesa_Language_Manual_Version_5.0.pdf 189 Mesa ~\cite{Mesa} is one programming languages that did.Experience with Mesa190 is quoted as being one of the reasons resumptions are not193 Mesa is one programming language that did.\cite{Mesa} Experience with Mesa 194 is quoted as being one of the reasons resumptions were not 191 195 included in the \Cpp standard. 192 196 % https://en.wikipedia.org/wiki/Exception_handling 193 As a result, resumption has ignored in main-stream programming languages. 194 However, ``what goes around comes around'' and resumption is being revisited now (like user-level threading). 195 While rejecting resumption might have been the right decision in the past, there are decades 196 of developments in computer science that have changed the situation. 197 Some of these developments, such as functional programming's resumption 198 equivalent, algebraic effects\cite{Zhang19}, are enjoying significant success. 199 A complete reexamination of resumptions is beyond this thesis, but their re-emergence is 200 enough to try them in \CFA. 197 Since then, resumptions have been ignored in mainstream programming languages. 198 However, resumption is being revisited in the context of decades of other 199 developments in programming languages. 200 While rejecting resumption may have been the right decision in the past, 201 the situation has changed since then. 202 Some developments, such as the functional programming equivalent to resumptions, 203 algebraic effects\cite{Zhang19}, are enjoying success. 204 A complete reexamination of resumption is beyond this thesis, 205 but their reemergence is enough reason to try them in \CFA. 201 206 % Especially considering how much easier they are to implement than 202 % termination exceptions .203 204 %\subsection 205 Functional languages tend to use other solutions for their primary EHM,206 but exception-like constructs still appear.207 Termination appears in error construct, which marks the result of an208 expression as an error; the reafter, the result of any expression that tries to use it is also an209 error, and so on until an appropriate handler is reached.207 % termination exceptions and how much Peter likes them. 208 209 %\subsection 210 Functional languages tend to use other solutions for their primary error 211 handling mechanism, but exception-like constructs still appear. 212 Termination appears in the error construct, which marks the result of an 213 expression as an error; then the result of any expression that tries to use 214 it also results in an error, and so on until an appropriate handler is reached. 210 215 Resumption appears in algebraic effects, where a function dispatches its 211 216 side-effects to its caller for handling. 212 217 213 218 %\subsection 214 Some programming languages have moved to a restricted kind of EHM 215 called``panic".216 In Rust ~\cite{Rust}, a panic is just a program level abort that may be implemented by217 unwinding the stack like in termination exception handling.218 % https://doc.rust-lang.org/std/panic/fn.catch_unwind.html 219 In Go~\cite{Go}, a panicis very similar to a termination, except it only supports219 More recently, exceptions seem to be vanishing from newer programming 220 languages, replaced by ``panic". 221 In Rust, a panic is just a program level abort that may be implemented by 222 unwinding the stack like in termination exception 223 handling.\cite{RustPanicMacro}\cite{RustPanicModule} 224 Go's panic though is very similar to a termination, except it only supports 220 225 a catch-all by calling \code{Go}{recover()}, simplifying the interface at 221 the cost of flexibility. 222 223 %\subsection 224 Whileexception handling's most common use cases are in error handling,225 here are other ways to handle errors with comparisons toexceptions.226 the cost of flexibility.\cite{Go:2021} 227 228 %\subsection 229 As exception handling's most common use cases are in error handling, 230 here are some other ways to handle errors with comparisons with exceptions. 226 231 \begin{itemize} 227 232 \item\emph{Error Codes}: 228 This pattern has a function return an enumeration (or just a set of fixed values) to indicate 229 if an error occurred and possibly which error it was. 230 231 Error codes mix exceptional and normal values, artificially enlarging the type and/or value range. 232 Some languages address this issue by returning multiple values or a tuple, separating the error code from the function result. 233 However, the main issue with error codes is forgetting to checking them, 233 This pattern has a function return an enumeration (or just a set of fixed 234 values) to indicate if an error has occurred and possibly which error it was. 235 236 Error codes mix exceptional/error and normal values, enlarging the range of 237 possible return values. This can be addressed with multiple return values 238 (or a tuple) or a tagged union. 239 However, the main issue with error codes is forgetting to check them, 234 240 which leads to an error being quietly and implicitly ignored. 235 Some new languages have tools that issue warnings, if the error code is 236 discarded to avoid this problem. 237 Checking error codes also results in bloating the main execution path, especially if an error is not dealt with locally and has to be cascaded down the call stack to a higher-level function.. 241 Some new languages and tools will try to issue warnings when an error code 242 is discarded to avoid this problem. 243 Checking error codes also bloats the main execution path, 244 especially if the error is not handled immediately and has to be passed 245 through multiple functions before it is addressed. 246 247 Here is an example of the pattern in Bash, where commands can only ``return" 248 numbers and most output is done through streams of text. 249 \begin{lstlisting}[language=bash,escapechar={}] 250 # Immediately after running a command: 251 case $? in 252 0) 253 # Success 254 ;; 255 1) 256 # Error Code 1 257 ;; 258 2|3) 259 # Error Code 2 or Error Code 3 260 ;; 261 # Add more cases as needed. 262 asac 263 \end{lstlisting} 238 264 239 265 \item\emph{Special Return with Global Store}: 240 Some functions only return a boolean indicating success or failure 241 and store the exact reason for the error in a fixed global location. 242 For example, many C routines return non-zero or -1, indicating success or failure, 243 and write error details into the C standard variable @errno@. 244 245 This approach avoids the multiple results issue encountered with straight error codes 246 but otherwise has many (if not more) of the disadvantages. 247 For example, everything that uses the global location must agree on all possible errors and global variable are unsafe with concurrency. 266 Similar to the error codes pattern but the function itself only returns 267 that there was an error, 268 and stores the reason for the error in a fixed global location. 269 For example, many routines in the C standard library will only return some 270 error value (such as -1 or a null pointer) and the error code is written into 271 the standard variable @errno@. 272 273 This approach avoids the multiple results issue encountered with straight 274 error codes as only a single error value has to be returned, 275 but otherwise has the same disadvantages and more. 276 Every function that reads or writes to the global store must agree on all 277 possible errors and managing it becomes more complex with concurrency. 278 279 This example shows some of what has to be done to robustly handle a C 280 standard library function that reports errors this way. 281 \begin{lstlisting}[language=C] 282 // Now a library function can set the error. 283 int handle = open(path_name, flags); 284 if (-1 == handle) { 285 switch (errno) { 286 case ENAMETOOLONG: 287 // path_name is a bad argument. 288 break; 289 case ENFILE: 290 // A system resource has been exausted. 291 break; 292 // And many more... 293 } 294 } 295 \end{lstlisting} 296 % cite open man page? 248 297 249 298 \item\emph{Return Union}: … … 251 300 Success is one tag and the errors are another. 252 301 It is also possible to make each possible error its own tag and carry its own 253 additional information, but the two branch format is easy to make generic302 additional information, but the two-branch format is easy to make generic 254 303 so that one type can be used everywhere in error handling code. 255 304 256 This pattern is very popular in functional or any semi-functional language with 257 primitive support for tagged unions (or algebraic data types). 258 % We need listing Rust/rust to format code snipits from it. 305 This pattern is very popular in any functional or semi-functional language 306 with primitive support for tagged unions (or algebraic data types). 307 Return unions can also be expressed as monads (evaluation in a context) 308 and often are in languages with special syntax for monadic evaluation, 309 such as Haskell's \code{haskell}{do} blocks. 310 311 The main advantage is that an arbitrary object can be used to represent an 312 error, so it can include a lot more information than a simple error code. 313 The disadvantages include that the it does have to be checked along the main 314 execution, and if there aren't primitive tagged unions proper, usage can be 315 hard to enforce. 316 % We need listing Rust/rust to format code snippets from it. 259 317 % Rust's \code{rust}{Result<T, E>} 260 The main advantage is providing for more information about an 261 error, other than one of a fix-set of ids. 262 While some languages use checked union access to force error-code checking, 263 it is still possible to bypass the checking. 264 The main disadvantage is again significant error code on the main execution path and cascading through called functions. 318 319 This is a simple example of examining the result of a failing function in 320 Haskell, using its \code{haskell}{Either} type. 321 Examining \code{haskell}{error} further would likely involve more matching, 322 but the type of \code{haskell}{error} is user defined so there are no 323 general cases. 324 \begin{lstlisting}[language=haskell] 325 case failingFunction argA argB of 326 Right value -> -- Use the successful computed value. 327 Left error -> -- Handle the produced error. 328 \end{lstlisting} 329 330 Return unions as monads will result in the same code, but can hide most 331 of the work to propagate errors in simple cases. The code to actually handle 332 the errors, or to interact with other monads (a common case in these 333 languages) still has to be written by hand. 334 335 If \code{haskell}{failingFunction} is implemented with two helpers that 336 use the same error type, then it can be implemented with a \code{haskell}{do} 337 block. 338 \begin{lstlisting}[language=haskell,literate={}] 339 failingFunction x y = do 340 z <- helperOne x 341 helperTwo y z 342 \end{lstlisting} 265 343 266 344 \item\emph{Handler Functions}: 267 This pattern implicitly associates functions with errors.268 On error, the function that produced the error implicitlycalls another function to345 This pattern associates errors with functions. 346 On error, the function that produced the error calls another function to 269 347 handle it. 270 348 The handler function can be provided locally (passed in as an argument, 271 349 either directly as as a field of a structure/object) or globally (a global 272 350 variable). 273 C++ uses this approach as its fallback system if exception handling fails, \eg 274 \snake{std::terminate_handler} and for a time \snake{std::unexpected_handler} 275 276 Handler functions work a lot like resumption exceptions, without the dynamic handler search. 277 Therefore, setting setting up the handler can be more complex/expensive, especially if the handle must be passed through multiple function calls, but cheaper to call $O(1)$, and hence, 278 are more suited to frequent exceptional situations. 279 % The exception being global handlers if they are rarely change as the time 280 % in both cases shrinks towards zero. 351 C++ uses this approach as its fallback system if exception handling fails, 352 such as \snake{std::terminate} and, for a time, 353 \snake{std::unexpected}.\footnote{\snake{std::unexpected} was part of the 354 Dynamic Exception Specification, which has been removed from the standard 355 as of C++20.\cite{CppExceptSpec}} 356 357 Handler functions work a lot like resumption exceptions, 358 but without the dynamic search for a handler. 359 Since setting up the handler can be more complex/expensive, 360 especially when the handler has to be passed through multiple layers of 361 function calls, but cheaper (constant time) to call, 362 they are more suited to more frequent (less exceptional) situations. 363 Although, in \Cpp and other languages that do not have checked exceptions, 364 they can actually be enforced by the type system be more reliable. 365 366 This is a more local example in \Cpp, using a function to provide 367 a default value for a mapping. 368 \begin{lstlisting}[language=C++] 369 ValueT Map::key_or_default(KeyT key, ValueT(*make_default)(KeyT)) { 370 ValueT * value = find_value(key); 371 if (nullptr != value) { 372 return *value; 373 } else { 374 return make_default(key); 375 } 376 } 377 \end{lstlisting} 281 378 \end{itemize} 282 379 283 380 %\subsection 284 381 Because of their cost, exceptions are rarely used for hot paths of execution. 285 Therefore, there is an element of self-fulfilling prophecy for implementation 286 techniques to make exceptions cheap to set-up at the cost 287 of expensive usage. 288 This cost differential is less important in higher-level scripting languages, where use of exceptions for other tasks is more common. 289 An iconic example is Python's @StopIteration@ exception that is thrown by 290 an iterator to indicate that it is exhausted, especially when combined with Python's heavy 291 use of the iterator-based for-loop. 292 % https://docs.python.org/3/library/exceptions.html#StopIteration 382 Hence, there is an element of self-fulfilling prophecy as implementation 383 techniques have been focused on making them cheap to set up, 384 happily making them expensive to use in exchange. 385 This difference is less important in higher-level scripting languages, 386 where using exceptions for other tasks is more common. 387 An iconic example is Python's 388 \code{Python}{StopIteration}\cite{PythonExceptions} exception, that 389 is thrown by an iterator to indicate that it is exhausted. 390 When paired with Python's iterator-based for-loop, this will be thrown every 391 time the end of the loop is reached.\cite{PythonForLoop} -
doc/theses/andrew_beach_MMath/performance.tex
rf95634e rb7fd9daf 3 3 4 4 Performance is of secondary importance for most of this project. 5 Instead, the focus is to get the features working. The only performance5 Instead, the focus was to get the features working. The only performance 6 6 requirement is to ensure the tests for correctness run in a reasonable 7 amount of time. 7 amount of time. Hence, only a few basic performance tests were performed to 8 check this requirement. 8 9 9 10 \section{Test Set-Up} 10 11 Tests were run in \CFA, C++, Java and Python. 11 12 In addition there are two sets of tests for \CFA, 12 one for termination and one for resumption exceptions.13 14 C++ is the most comparable language because both it and \CFA use the same13 one with termination and one with resumption. 14 15 GCC C++ is the most comparable language because both it and \CFA use the same 15 16 framework, libunwind. 16 In fact, the comparison is almost entirely a quality of implementation17 comparison:\CFA's EHM has had significantly less time to be optimized and17 In fact, the comparison is almost entirely in quality of implementation. 18 Specifically, \CFA's EHM has had significantly less time to be optimized and 18 19 does not generate its own assembly. It does have a slight advantage in that 19 there are some features it handles directly instead of throughutility functions,20 but otherwise \Cpp hasa significant advantage.21 22 Java is another very popular language with similar termination semantics.23 Itis implemented in a very different environment, a virtual machine with20 \Cpp has to do some extra bookkeeping to support its utility functions, 21 but otherwise \Cpp should have a significant advantage. 22 23 Java, a popular language with similar termination semantics, 24 is implemented in a very different environment, a virtual machine with 24 25 garbage collection. 25 It also implements the @finally@ clause on @try@blocks allowing for a direct26 It also implements the finally clause on try blocks allowing for a direct 26 27 feature-to-feature comparison. 27 As with \Cpp, Java's implementation is mature, optimizations28 and has extra features.29 30 Python is used as an alternative point ofcomparison because of the \CFA EHM's31 current performance goals, which is not tobe prohibitively slow while the28 As with \Cpp, Java's implementation is mature, has more optimizations 29 and extra features as compared to \CFA. 30 31 Python is used as an alternative comparison because of the \CFA EHM's 32 current performance goals, which is to not be prohibitively slow while the 32 33 features are designed and examined. Python has similar performance goals for 33 34 creating quick scripts and its wide use suggests it has achieved those goals. … … 36 37 resumption exceptions. Even the older programming languages with resumption 37 38 seem to be notable only for having resumption. 38 So instead, resumption is compared to a less similar but much more familiar 39 feature, termination exceptions. 39 On the other hand, the functional equivalents to resumption are too new. 40 There does not seem to be any standard implementations in well-known 41 languages; so far, they seem confined to extensions and research languages. 42 % There was some maybe interesting comparison to an OCaml extension 43 % but I'm not sure how to get that working if it is interesting. 44 Instead, resumption is compared to its simulation in other programming 45 languages: fixup functions that are explicitly passed into a function. 40 46 41 47 All tests are run inside a main loop that repeatedly performs a test. 42 48 This approach avoids start-up or tear-down time from 43 49 affecting the timing results. 44 Each test is run a million times. 45 The Java versions of the test run this loop an extra 1000 times before 46 beginning to actual test to ``warm-up" the JVM. 50 The number of times the loop is run is configurable from the command line; 51 the number used in the timing runs is given with the results per test. 52 The Java tests run the main loop 1000 times before 53 beginning the actual test to ``warm up" the JVM. 54 % All other languages are precompiled or interpreted. 47 55 48 56 Timing is done internally, with time measured immediately before and … … 51 59 unhandled exceptions in \Cpp and Java as that would cause the process to 52 60 terminate. 53 Luckily, performance on the ``give -up and kill the process" path is not61 Luckily, performance on the ``give up and kill the process" path is not 54 62 critical. 55 63 56 64 The exceptions used in these tests are always based off of 57 a base exception. This requirement minimizes performance differences based 65 the base exception for the language. 66 This requirement minimizes performance differences based 58 67 on the object model used to represent the exception. 59 68 … … 62 71 For example, empty inline assembly blocks are used in \CFA and \Cpp to 63 72 prevent excessive optimizations while adding no actual work. 64 Each test was run eleven times. The top three and bottom three results were65 discarded and the remaining five values are averaged.66 67 The tests are compiled with gcc-10 for \CFA and g++-10 for \Cpp. Java is68 compiled with 11.0.11. Python with 3.8. The tests were run on:69 \begin{itemize}[nosep]70 \item71 ARM 2280 Kunpeng 920 48-core 2$\times$socket \lstinline{@} 2.6 GHz running Linux v5.11.0-2572 \item73 AMD 6380 Abu Dhabi 16-core 4$\times$socket \lstinline{@} 2.5 GHz running Linux v5.11.0-2574 \end{itemize}75 73 76 74 % We don't use catch-alls but if we did: 77 75 % Catch-alls are done by catching the root exception type (not using \Cpp's 78 76 % \code{C++}{catch(...)}). 77 78 When collecting data, each test is run eleven times. The top three and bottom 79 three results are discarded and the remaining five values are averaged. 80 The test are run with the latest (still pre-release) \CFA compiler, 81 using gcc-10 10.3.0 as a backend. 82 g++-10 10.3.0 is used for \Cpp. 83 Java tests are complied and run with Oracle OpenJDK version 11.0.11. 84 Python used CPython version 3.8.10. 85 The machines used to run the tests are: 86 \begin{itemize}[nosep] 87 \item ARM 2280 Kunpeng 920 48-core 2$\times$socket 88 \lstinline{@} 2.6 GHz running Linux v5.11.0-25 89 \item AMD 6380 Abu Dhabi 16-core 4$\times$socket 90 \lstinline{@} 2.5 GHz running Linux v5.11.0-25 91 \end{itemize} 92 These represent the two major families of hardware architecture. 79 93 80 94 \section{Tests} … … 83 97 They should provide a guide as to where the EHM's costs are found. 84 98 85 \paragraph{Raise and Handle} 86 The first group measures the cost of a try statement when exceptions are raised 87 and \emph{the stack is unwound}. Each test has has a repeating function like 88 the following 99 \paragraph{Stack Traversal} 100 This group of tests measures the cost of traversing the stack 101 (and in termination, unwinding it). 102 Inside the main loop is a call to a recursive function. 103 This function calls itself F times before raising an exception. 104 F is configurable from the command line, but is usually 100. 105 This builds up many stack frames, and any contents they may have, 106 before the raise. 107 The exception is always handled at the base of the stack. 108 For example the Empty test for \CFA resumption looks like: 89 109 \begin{cfa} 90 110 void unwind_empty(unsigned int frames) { 91 111 if (frames) { 92 112 unwind_empty(frames - 1); 93 } else throw (empty_exception){&empty_vt}; 113 } else { 114 throwResume (empty_exception){&empty_vt}; 115 } 94 116 } 95 117 \end{cfa} 96 which is called M times, where each call recurses to a depth of N, an 97 exception is raised, the stack is a unwound, and the exception caught. 118 Other test cases have additional code around the recursive call adding 119 something besides simple stack frames to the stack. 120 Note that both termination and resumption have to traverse over 121 the stack but only termination has to unwind it. 98 122 \begin{itemize}[nosep] 123 % \item None: 124 % Reuses the empty test code (see below) except that the number of frames 125 % is set to 0 (this is the only test for which the number of frames is not 126 % 100). This isolates the start-up and shut-down time of a throw. 99 127 \item Empty: 100 This test measures the cost of raising (stack walking) an exception through empty 101 empty stack frames to an empty handler. (see above) 128 The repeating function is empty except for the necessary control code. 129 As other traversal tests add to this, it is the baseline for the group 130 as the cost comes from traversing over and unwinding a stack frame 131 that has no other interactions with the exception system. 102 132 \item Destructor: 103 104 This test measures the cost of raising an exception through non-empty frames 105 where each frame has an object requiring destruction, to an empty 106 handler. Hence, there are N destructor calls during unwinding. 107 \begin{cfa} 108 if (frames) { 109 WithDestructor object; 110 unwind_empty(frames - 1); 111 \end{cfa} 133 The repeating function creates an object with a destructor before calling 134 itself. 135 Comparing this to the empty test gives the time to traverse over and 136 unwind a destructor. 112 137 \item Finally: 113 This test measures the cost of establishing a try block with an empty finally 114 clause on the front side of the recursion and running the empty finally clause 115 on the back side of the recursion during stack unwinding. 116 \begin{cfa} 117 if (frames) { 118 try { 119 unwind_finally(frames - 1); 120 } finally {} 121 \end{cfa} 138 The repeating function calls itself inside a try block with a finally clause 139 attached. 140 Comparing this to the empty test gives the time to traverse over and 141 unwind a finally clause. 122 142 \item Other Handler: 123 This test is like the finally test but the try block has a catch clause for an 124 exception that is not raised, so catch matching is executed during stack 125 unwinding but the match never successes until the catch at the bottom of the 126 stack. 127 \begin{cfa} 128 if (frames) { 129 try { 130 unwind_other(frames - 1); 131 } catch (not_raised_exception *) {} 132 \end{cfa} 143 The repeating function calls itself inside a try block with a handler that 144 does not match the raised exception, but is of the same kind of handler. 145 This means that the EHM has to check each handler, and continue 146 over all of them until it reaches the base of the stack. 147 Comparing this to the empty test gives the time to traverse over and 148 unwind a handler. 133 149 \end{itemize} 134 150 135 151 \paragraph{Cross Try Statement} 136 The next group measures just the cost of executing a try statement so 137 \emph{there is no stack unwinding}. Hence, the program main loops N times 138 around: 152 This group of tests measures the cost for setting up exception handling, 153 if it is 154 not used because the exceptional case did not occur. 155 Tests repeatedly cross (enter, execute and leave) a try statement but never 156 perform a raise. 157 \begin{itemize}[nosep] 158 \item Handler: 159 The try statement has a handler (of the appropriate kind). 160 \item Finally: 161 The try statement has a finally clause. 162 \end{itemize} 163 164 \paragraph{Conditional Matching} 165 This group measures the cost of conditional matching. 166 Only \CFA implements the language level conditional match, 167 the other languages mimic it with an ``unconditional" match (it still 168 checks the exception's type) and conditional re-raise if it is not supposed 169 to handle that exception. 170 171 Here is the pattern shown in \CFA and \Cpp. Java and Python use the same 172 pattern as \Cpp, but with their own syntax. 173 174 \begin{minipage}{0.45\textwidth} 139 175 \begin{cfa} 140 176 try { 141 } catch (not_raised_exception *) {} 142 \end{cfa} 143 \begin{itemize}[nosep] 144 \item Handler: 145 The try statement has a handler. 146 \item Finally: 147 The try statement replaces the handler with a finally clause. 148 \end{itemize} 149 150 \paragraph{Conditional Matching} 151 This final group measures the cost of conditional matching. 152 Only \CFA implements the language level conditional match, 153 the other languages must mimic with an ``unconditional" match (it still 154 checks the exception's type) and conditional re-raise if it was not supposed 155 to handle that exception. 156 \begin{center} 157 \begin{tabular}{ll} 158 \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp, Java, Python} \\ 159 \begin{cfa} 160 try { 161 throw_exception(); 162 } catch (empty_exception * exc; 163 should_catch) { 177 ... 178 } catch (exception_t * e ; 179 should_catch(e)) { 180 ... 164 181 } 165 182 \end{cfa} 166 & 167 \begin{cfa} 183 \end{minipage} 184 \begin{minipage}{0.55\textwidth} 185 \begin{lstlisting}[language=C++] 168 186 try { 169 throw_exception(); 170 } catch (EmptyException & exc) { 171 if (!should_catch) throw; 187 ... 188 } catch (std::exception & e) { 189 if (!should_catch(e)) throw; 190 ... 172 191 } 173 \end{cfa} 174 \end{tabular} 175 \end{center} 192 \end{lstlisting} 193 \end{minipage} 176 194 \begin{itemize}[nosep] 177 195 \item Match All: … … 180 198 The condition is always false. (Never matches or always re-raises.) 181 199 \end{itemize} 200 201 \paragraph{Resumption Simulation} 202 A slightly altered version of the Empty Traversal test is used when comparing 203 resumption to fix-up routines. 204 The handler, the actual resumption handler or the fix-up routine, 205 always captures a variable at the base of the loop, 206 and receives a reference to a variable at the raise site, either as a 207 field on the exception or an argument to the fix-up routine. 208 % I don't actually know why that is here but not anywhere else. 182 209 183 210 %\section{Cost in Size} … … 192 219 193 220 \section{Results} 194 In cases where a feature is not supported by a language the test is skipped 195 for that language. 196 \PAB{Report all values. 197 198 Similarly, if a test does not change between resumption 199 and termination in \CFA, then only one test is written and the result 200 was put into the termination column. 201 } 202 203 % Raw Data: 204 % run-algol-a.sat 205 % --------------- 206 % Raise Empty & 82687046678 & 291616256 & 3252824847 & 15422937623 & 14736271114 \\ 207 % Raise D'tor & 219933199603 & 297897792 & 223602799362 & N/A & N/A \\ 208 % Raise Finally & 219703078448 & 298391745 & N/A & ... & 18923060958 \\ 209 % Raise Other & 296744104920 & 2854342084 & 112981255103 & 15475924808 & 21293137454 \\ 210 % Cross Handler & 9256648 & 13518430 & 769328 & 3486252 & 31790804 \\ 211 % Cross Finally & 769319 & N/A & N/A & 2272831 & 37491962 \\ 212 % Match All & 3654278402 & 47518560 & 3218907794 & 1296748192 & 624071886 \\ 213 % Match None & 4788861754 & 58418952 & 9458936430 & 1318065020 & 625200906 \\ 214 % 215 % run-algol-thr-c 216 % --------------- 217 % Raise Empty & 3757606400 & 36472972 & 3257803337 & 15439375452 & 14717808642 \\ 218 % Raise D'tor & 64546302019 & 102148375 & 223648121635 & N/A & N/A \\ 219 % Raise Finally & 64671359172 & 103285005 & N/A & 15442729458 & 18927008844 \\ 220 % Raise Other & 294143497130 & 2630130385 & 112969055576 & 15448220154 & 21279953424 \\ 221 % Cross Handler & 9646462 & 11955668 & 769328 & 3453707 & 31864074 \\ 222 % Cross Finally & 773412 & N/A & N/A & 2253825 & 37266476 \\ 223 % Match All & 3719462155 & 43294042 & 3223004977 & 1286054154 & 623887874 \\ 224 % Match None & 4971630929 & 55311709 & 9481225467 & 1310251289 & 623752624 \\ 225 % 226 % run-algol-04-a 227 % -------------- 228 % Raise Empty & 0.0 & 0.0 & 3250260945 & 0.0 & 0.0 \\ 229 % Raise D'tor & 0.0 & 0.0 & 29017675113 & N/A & N/A \\ 230 % Raise Finally & 0.0 & 0.0 & N/A & 0.0 & 0.0 \\ 231 % Raise Other & 0.0 & 0.0 & 24411823773 & 0.0 & 0.0 \\ 232 % Cross Handler & 0.0 & 0.0 & 769334 & 0.0 & 0.0 \\ 233 % Cross Finally & 0.0 & N/A & N/A & 0.0 & 0.0 \\ 234 % Match All & 0.0 & 0.0 & 3254283504 & 0.0 & 0.0 \\ 235 % Match None & 0.0 & 0.0 & 9476060146 & 0.0 & 0.0 \\ 236 237 \begin{tabular}{|l|c c c c c|} 238 \hline 239 & \CFA (Terminate) & \CFA (Resume) & \Cpp & Java & Python \\ 240 \hline 241 Raise Empty & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 242 Raise D'tor & 0.0 & 0.0 & 0.0 & N/A & N/A \\ 243 Raise Finally & 0.0 & 0.0 & N/A & 0.0 & 0.0 \\ 244 Raise Other & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 245 Cross Handler & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 246 Cross Finally & 0.0 & N/A & N/A & 0.0 & 0.0 \\ 247 Match All & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 248 Match None & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 221 % First, introduce the tables. 222 \autoref{t:PerformanceTermination}, 223 \autoref{t:PerformanceResumption} 224 and~\autoref{t:PerformanceFixupRoutines} 225 show the test results. 226 In cases where a feature is not supported by a language, the test is skipped 227 for that language and the result is marked N/A. 228 There are also cases where the feature is supported but measuring its 229 cost is impossible. This happened with Java, which uses a JIT that optimizes 230 away the tests and cannot be stopped.\cite{Dice21} 231 These tests are marked N/C. 232 To get results in a consistent range (1 second to 1 minute is ideal, 233 going higher is better than going low) N, the number of iterations of the 234 main loop in each test, is varied between tests. It is also given in the 235 results and has a value in the millions. 236 237 An anomaly in some results came from \CFA's use of GCC nested functions. 238 These nested functions are used to create closures that can access stack 239 variables in their lexical scope. 240 However, if they do so, then they can cause the benchmark's run time to 241 increase by an order of magnitude. 242 The simplest solution is to make those values global variables instead 243 of function-local variables. 244 % Do we know if editing a global inside nested function is a problem? 245 Tests that had to be modified to avoid this problem have been marked 246 with a ``*'' in the results. 247 248 % Now come the tables themselves: 249 % You might need a wider window for this. 250 251 \begin{table}[htb] 252 \centering 253 \caption{Termination Performance Results (sec)} 254 \label{t:PerformanceTermination} 255 \begin{tabular}{|r|*{2}{|r r r r|}} 256 \hline 257 & \multicolumn{4}{c||}{AMD} & \multicolumn{4}{c|}{ARM} \\ 258 \cline{2-9} 259 N\hspace{8pt} & \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c||}{Python} & 260 \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c|}{Python} \\ 261 \hline 262 Empty Traversal (1M) & 23.0 & 9.6 & 17.6 & 23.4 & 30.6 & 13.6 & 15.5 & 14.7 \\ 263 D'tor Traversal (1M) & 48.1 & 23.5 & N/A & N/A & 64.2 & 29.2 & N/A & N/A \\ 264 Finally Traversal (1M) & 3.2* & N/A & 17.6 & 29.2 & 3.9* & N/A & 15.5 & 19.0 \\ 265 Other Traversal (1M) & 3.3* & 23.9 & 17.7 & 32.8 & 3.9* & 24.5 & 15.5 & 21.6 \\ 266 Cross Handler (1B) & 6.5 & 0.9 & N/C & 38.0 & 9.6 & 0.8 & N/C & 32.1 \\ 267 Cross Finally (1B) & 0.8 & N/A & N/C & 44.6 & 0.6 & N/A & N/C & 37.3 \\ 268 Match All (10M) & 30.5 & 20.6 & 11.2 & 3.9 & 36.9 & 24.6 & 10.7 & 3.1 \\ 269 Match None (10M) & 30.6 & 50.9 & 11.2 & 5.0 & 36.9 & 71.9 & 10.7 & 4.1 \\ 249 270 \hline 250 271 \end{tabular} 251 252 % run-plg7a-a.sat 253 % --------------- 254 % Raise Empty & 57169011329 & 296612564 & 2788557155 & 17511466039 & 23324548496 \\ 255 % Raise D'tor & 150599858014 & 318443709 & 149651693682 & N/A & N/A \\ 256 % Raise Finally & 148223145000 & 373325807 & N/A & ... & 29074552998 \\ 257 % Raise Other & 189463708732 & 3017109322 & 85819281694 & 17584295487 & 32602686679 \\ 258 % Cross Handler & 8001654 & 13584858 & 1555995 & 6626775 & 41927358 \\ 259 % Cross Finally & 1002473 & N/A & N/A & 4554344 & 51114381 \\ 260 % Match All & 3162460860 & 37315018 & 2649464591 & 1523205769 & 742374509 \\ 261 % Match None & 4054773797 & 47052659 & 7759229131 & 1555373654 & 744656403 \\ 262 % 263 % run-plg7a-thr-a 264 % --------------- 265 % Raise Empty & 3604235388 & 29829965 & 2786931833 & 17576506385 & 23352975105 \\ 266 % Raise D'tor & 46552380948 & 178709605 & 149834207219 & N/A & N/A \\ 267 % Raise Finally & 46265157775 & 177906320 & N/A & 17493045092 & 29170962959 \\ 268 % Raise Other & 195659245764 & 2376968982 & 86070431924 & 17552979675 & 32501882918 \\ 269 % Cross Handler & 397031776 & 12503552 & 1451225 & 6658628 & 42304965 \\ 270 % Cross Finally & 1136746 & N/A & N/A & 4468799 & 46155817 \\ 271 % Match All & 3189512499 & 39124453 & 2667795989 & 1525889031 & 733785613 \\ 272 % Match None & 4094675477 & 48749857 & 7850618572 & 1566713577 & 733478963 \\ 273 % 274 % run-plg7a-04-a 275 % -------------- 276 % 0.0 are unfilled. 277 % Raise Empty & 0.0 & 0.0 & 2770781479 & 0.0 & 0.0 \\ 278 % Raise D'tor & 0.0 & 0.0 & 23530084907 & N/A & N/A \\ 279 % Raise Finally & 0.0 & 0.0 & N/A & 0.0 & 0.0 \\ 280 % Raise Other & 0.0 & 0.0 & 23816827982 & 0.0 & 0.0 \\ 281 % Cross Handler & 0.0 & 0.0 & 1422188 & 0.0 & 0.0 \\ 282 % Cross Finally & 0.0 & N/A & N/A & 0.0 & 0.0 \\ 283 % Match All & 0.0 & 0.0 & 2671989778 & 0.0 & 0.0 \\ 284 % Match None & 0.0 & 0.0 & 7829059869 & 0.0 & 0.0 \\ 285 286 % PLG7A (in seconds) 287 \begin{tabular}{|l|c c c c c|} 288 \hline 289 & \CFA (Terminate) & \CFA (Resume) & \Cpp & Java & Python \\ 290 \hline 291 Raise Empty & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 292 Raise D'tor & 0.0 & 0.0 & 0.0 & N/A & N/A \\ 293 Raise Finally & 0.0 & 0.0 & N/A & 0.0 & 0.0 \\ 294 Raise Other & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 295 Cross Handler & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 296 Cross Finally & 0.0 & N/A & N/A & 0.0 & 0.0 \\ 297 Match All & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 298 Match None & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 272 \end{table} 273 274 \begin{table}[htb] 275 \centering 276 \caption{Resumption Performance Results (sec)} 277 \label{t:PerformanceResumption} 278 \begin{tabular}{|r||r||r|} 279 \hline 280 N\hspace{8pt} 281 & AMD & ARM \\ 282 \hline 283 Empty Traversal (10M) & 1.4 & 1.2 \\ 284 D'tor Traversal (10M) & 1.8 & 1.0 \\ 285 Finally Traversal (10M) & 1.8 & 1.0 \\ 286 Other Traversal (10M) & 22.6 & 25.8 \\ 287 Cross Handler (1B) & 9.0 & 11.9 \\ 288 Match All (100M) & 2.3 & 3.2 \\ 289 Match None (100M) & 3.0 & 3.8 \\ 299 290 \hline 300 291 \end{tabular} 301 302 One result not directly related to \CFA but important to keep in 303 mind is that, for exceptions, the standard intuition about which languages 304 should go faster often does not hold. For example, there are cases where Python out-performs 305 \Cpp and Java. The most likely explanation is that, since exceptions are 306 rarely considered to be the common case, the more optimized languages 307 make that case expense. In addition, languages with high-level 308 representations have a much easier time scanning the stack as there is less 309 to decode. 310 311 This observation means that while \CFA does not actually keep up with Python in every 312 case, it is usually no worse than roughly half the speed of \Cpp. This performance is good 313 enough for the prototyping purposes of the project. 314 315 The test case where \CFA falls short is Raise Other, the case where the 316 stack is unwound including a bunch of non-matching handlers. 317 This slowdown seems to come from missing optimizations. 318 319 Importantly, there is a huge slowdown in \Cpp's results bringing that brings 320 \CFA's performance back in that roughly half speed area. However many other 321 \CFA benchmarks increase their run-time by a similar amount falling far 322 behind their \Cpp counter-parts. 323 324 This suggests that the performance issue in Raise Other is just an 325 optimization not being applied. Later versions of gcc may be able to 326 optimize this case further, at least down to the half of \Cpp mark. 327 A \CFA compiler that directly produced assembly could do even better as it 328 would not have to work across some of \CFA's current abstractions, like 329 the try terminate function. 330 331 Resumption exception handling is also incredibly fast. Often an order of 332 magnitude or two better than the best termination speed. 333 There is a simple explanation for this; traversing a linked list is much 334 faster than examining and unwinding the stack. When resumption does not do as 335 well its when more try statements are used per raise. Updating the internal 336 linked list is not very expensive but it does add up. 337 338 The relative speed of the Match All and Match None tests (within each 339 language) can also show the effectiveness conditional matching as compared 340 to catch and rethrow. 341 \begin{itemize}[nosep] 342 \item 343 Java and Python get similar values in both tests. 344 Between the interpreted code, a higher level representation of the call 345 stack and exception reuse it it is possible the cost for a second 346 throw can be folded into the first. 347 % Is this due to optimization? 348 \item 349 Both types of \CFA are slightly slower if there is not a match. 350 For termination this likely comes from unwinding a bit more stack through 351 libunwind instead of executing the code normally. 352 For resumption there is extra work in traversing more of the list and running 353 more checks for a matching exceptions. 354 % Resumption is a bit high for that but this is my best theory. 355 \item 356 Then there is \Cpp, which takes 2--3 times longer to catch and rethrow vs. 357 just the catch. This is very high, but it does have to repeat the same 358 process of unwinding the stack and may have to parse the LSDA of the function 359 with the catch and rethrow twice, once before the catch and once after the 360 rethrow. 361 % I spent a long time thinking of what could push it over twice, this is all 362 % I have to explain it. 363 \end{itemize} 364 The difference in relative performance does show that there are savings to 365 be made by performing the check without catching the exception. 292 \end{table} 293 294 \begin{table}[htb] 295 \centering 296 \small 297 \caption{Resumption/Fixup Routine Comparison (sec)} 298 \label{t:PerformanceFixupRoutines} 299 \setlength{\tabcolsep}{5pt} 300 \begin{tabular}{|r|*{2}{|r r r r r|}} 301 \hline 302 & \multicolumn{5}{c||}{AMD} & \multicolumn{5}{c|}{ARM} \\ 303 \cline{2-11} 304 N\hspace{8pt} & \multicolumn{1}{c}{Raise} & \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c||}{Python} & 305 \multicolumn{1}{c}{Raise} & \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c|}{Python} \\ 306 \hline 307 Resume Empty (10M) & 1.4 & 1.4 & 15.4 & 2.3 & 178.0 & 1.2 & 1.2 & 8.9 & 1.2 & 118.4 \\ 308 \hline 309 \end{tabular} 310 \end{table} 311 312 % Now discuss the results in the tables. 313 One result not directly related to \CFA but important to keep in mind is that, 314 for exceptions, the standard intuition about which languages should go 315 faster often does not hold. 316 For example, there are a few cases where Python out-performs 317 \CFA, \Cpp and Java. 318 % To be exact, the Match All and Match None cases. 319 The most likely explanation is that 320 the generally faster languages have made ``common cases fast" at the expense 321 of the rarer cases. Since exceptions are considered rare, they are made 322 expensive to help speed up common actions, such as entering and leaving try 323 statements. 324 Python, on the other hand, while generally slower than the other languages, 325 uses exceptions more and has not sacrificed their performance. 326 In addition, languages with high-level representations have a much 327 easier time scanning the stack as there is less to decode. 328 329 As stated, 330 the performance tests are not attempting to show \CFA has a new competitive 331 way of implementing exception handling. 332 The only performance requirement is to insure the \CFA EHM has reasonable 333 performance for prototyping. 334 Although that may be hard to exactly quantify, I believe it has succeeded 335 in that regard. 336 Details on the different test cases follow. 337 338 \subsection{Termination \texorpdfstring{(\autoref{t:PerformanceTermination})}{}} 339 340 \begin{description} 341 \item[Empty Traversal] 342 \CFA is slower than \Cpp, but is still faster than the other languages 343 and closer to \Cpp than other languages. 344 This result is to be expected, 345 as \CFA is closer to \Cpp than the other languages. 346 347 \item[D'tor Traversal] 348 Running destructors causes a huge slowdown in the two languages that support 349 them. \CFA has a higher proportionate slowdown but it is similar to \Cpp's. 350 Considering the amount of work done in destructors is effectively zero 351 (an assembly comment), the cost 352 must come from the change of context required to run the destructor. 353 354 \item[Finally Traversal] 355 Performance is similar to Empty Traversal in all languages that support finally 356 clauses. Only Python seems to have a larger than random noise change in 357 its run time and it is still not large. 358 Despite the similarity between finally clauses and destructors, 359 finally clauses seem to avoid the spike that run time destructors have. 360 Possibly some optimization removes the cost of changing contexts. 361 362 \item[Other Traversal] 363 For \Cpp, stopping to check if a handler applies seems to be about as 364 expensive as stopping to run a destructor. 365 This results in a significant jump. 366 367 Other languages experience a small increase in run time. 368 The small increase likely comes from running the checks, 369 but they could avoid the spike by not having the same kind of overhead for 370 switching to the check's context. 371 372 \item[Cross Handler] 373 Here, \CFA falls behind \Cpp by a much more significant margin. 374 This is likely due to the fact that \CFA has to insert two extra function 375 calls, while \Cpp does not have to execute any other instructions. 376 Python is much further behind. 377 378 \item[Cross Finally] 379 \CFA's performance now matches \Cpp's from Cross Handler. 380 If the code from the finally clause is being inlined, 381 which is just an asm comment, than there are no additional instructions 382 to execute again when exiting the try statement normally. 383 384 \item[Conditional Match] 385 Both of the conditional matching tests can be considered on their own. 386 However, for evaluating the value of conditional matching itself, the 387 comparison of the two sets of results is useful. 388 Consider the massive jump in run time for \Cpp going from match all to match 389 none, which none of the other languages have. 390 Some strange interaction is causing run time to more than double for doing 391 twice as many raises. 392 Java and Python avoid this problem and have similar run time for both tests, 393 possibly through resource reuse or their program representation. 394 However, \CFA is built like \Cpp, and avoids the problem as well. 395 This matches 396 the pattern of the conditional match, which makes the two execution paths 397 very similar. 398 399 \end{description} 400 401 \subsection{Resumption \texorpdfstring{(\autoref{t:PerformanceResumption})}{}} 402 403 Moving on to resumption, there is one general note: 404 resumption is \textit{fast}. The only test where it fell 405 behind termination is Cross Handler. 406 In every other case, the number of iterations had to be increased by a 407 factor of 10 to get the run time in an appropriate range 408 and in some cases resumption still took less time. 409 410 % I tried \paragraph and \subparagraph, maybe if I could adjust spacing 411 % between paragraphs those would work. 412 \begin{description} 413 \item[Empty Traversal] 414 See above for the general speed-up notes. 415 This result is not surprising as resumption's linked-list approach 416 means that traversing over stack frames without a resumption handler is 417 $O(1)$. 418 419 \item[D'tor Traversal] 420 Resumption does have the same spike in run time that termination has. 421 The run time is actually very similar to Finally Traversal. 422 As resumption does not unwind the stack, both destructors and finally 423 clauses are run while walking down the stack during the recursive returns. 424 So it follows their performance is similar. 425 426 \item[Finally Traversal] 427 Same as D'tor Traversal, 428 except termination did not have a spike in run time on this test case. 429 430 \item[Other Traversal] 431 Traversing across handlers reduces resumption's advantage as it actually 432 has to stop and check each one. 433 Resumption still came out ahead (adjusting for iterations) but by much less 434 than the other cases. 435 436 \item[Cross Handler] 437 The only test case where resumption could not keep up with termination, 438 although the difference is not as significant as many other cases. 439 It is simply a matter of where the costs come from: 440 both termination and resumption have some work to set up or tear down a 441 handler. It just so happens that resumption's work is slightly slower. 442 443 \item[Conditional Match] 444 Resumption shows a slight slowdown if the exception is not matched 445 by the first handler, which follows from the fact the second handler now has 446 to be checked. However, the difference is not large. 447 448 \end{description} 449 450 \subsection{Resumption/Fixup \texorpdfstring{(\autoref{t:PerformanceFixupRoutines})}{}} 451 452 Finally are the results of the resumption/fixup routine comparison. 453 These results are surprisingly varied. It is possible that creating a closure 454 has more to do with performance than passing the argument through layers of 455 calls. 456 At 100 stack frames, resumption and manual fixup routines have similar 457 performance in \CFA. 458 More experiments could try to tease out the exact trade-offs, 459 but the prototype's only performance goal is to be reasonable. 460 It is already in that range, and \CFA's fixup routine simulation is 461 one of the faster simulations as well. 462 Plus, exceptions add features and remove syntactic overhead, 463 so even at similar performance, resumptions have advantages 464 over fixup routines. -
doc/theses/andrew_beach_MMath/resumption-marking.fig
rf95634e rb7fd9daf 8 8 -2 9 9 1200 2 10 6 5985 1530 6165 310511 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 1620 90 90 6075 1620 6075 171012 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 2340 90 90 6075 2340 6075 243013 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 3015 90 90 6075 3015 6075 310514 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 215 1 1 1.00 60.00 120.0016 6075 1755 6075 220517 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 218 1 1 1.00 60.00 120.0019 6075 2475 6075 292520 -621 6 3465 1530 3645 310522 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 1620 90 90 3555 1620 3555 171023 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 2340 90 90 3555 2340 3555 243024 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 3015 90 90 3555 3015 3555 310525 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 226 1 1 1.00 60.00 120.0027 3555 1755 3555 220528 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 229 1 1 1.00 60.00 120.0030 3555 2475 3555 292531 -632 6 2115 1530 2295 310533 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 1620 90 90 2205 1620 2205 171034 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 2340 90 90 2205 2340 2205 243035 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 3015 90 90 2205 3015 2205 310536 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 237 1 1 1.00 60.00 120.0038 2205 1755 2205 220539 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 240 1 1 1.00 60.00 120.0041 2205 2475 2205 292542 -643 10 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 1620 90 90 4905 1620 4905 1710 44 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 3015 90 90 4905 3015 4905 310545 11 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 945 90 90 4905 945 4905 1035 46 12 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 2340 90 90 4905 2340 4905 2430 47 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 48 1 1 1.00 60.00 120.00 49 2790 1620 2430 1620 50 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 51 1 1 1.00 60.00 120.00 52 4095 2340 3735 2340 53 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 54 1 1 1.00 60.00 120.00 55 6660 1620 6300 1620 56 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 57 1 1 1.00 60.00 120.00 58 5490 945 5130 945 13 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 1620 90 90 1665 1620 1665 1710 14 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 2340 90 90 1665 2340 1665 2430 15 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 3060 90 90 1665 3060 1665 3150 16 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 1620 90 90 3195 1620 3195 1710 17 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 2340 90 90 3195 2340 3195 2430 18 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 3060 90 90 3195 3060 3195 3150 19 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 1620 90 90 6525 1620 6525 1710 20 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 2340 90 90 6525 2340 6525 2430 21 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 3060 90 90 4905 3060 4905 3150 22 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 3060 90 90 6525 3060 6525 3150 59 23 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 60 24 1 1 1.00 60.00 120.00 … … 66 30 1 1 1.00 60.00 120.00 67 31 4770 1080 4590 1260 4590 2070 4770 2250 68 4 0 0 50 -1 0 12 0.0000 4 135 1170 1980 3375 Initial State\001 69 4 0 0 50 -1 0 12 0.0000 4 135 1170 3420 3375 Found Handler\001 70 4 0 0 50 -1 0 12 0.0000 4 165 810 4770 3375 Try block\001 71 4 0 0 50 -1 0 12 0.0000 4 135 900 4770 3555 in Handler\001 72 4 0 0 50 -1 0 12 0.0000 4 165 1530 5940 3375 Handling Complete\001 32 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 33 1 1 1.00 60.00 120.00 34 1665 1755 1665 2205 35 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 36 1 1 1.00 60.00 120.00 37 1665 2475 1665 2925 38 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 39 1 1 1.00 60.00 120.00 40 3195 1755 3195 2205 41 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 42 1 1 1.00 60.00 120.00 43 3195 2475 3195 2925 44 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 45 1 1 1.00 60.00 120.00 46 6525 1755 6525 2205 47 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 48 1 1 1.00 60.00 120.00 49 6525 2475 6525 2925 50 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 51 1 1 1.00 60.00 120.00 52 1260 1620 1485 1620 53 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 54 1 1 1.00 60.00 120.00 55 1980 1440 1755 1440 56 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 57 1 1 1.00 60.00 120.00 58 2790 2340 3015 2340 59 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 60 1 1 1.00 60.00 120.00 61 3600 1620 3375 1620 62 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 63 1 1 1.00 60.00 120.00 64 4500 945 4725 945 65 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 66 1 1 1.00 60.00 120.00 67 5265 765 5040 765 68 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 69 1 1 1.00 60.00 120.00 70 6120 1620 6345 1620 71 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 72 1 1 1.00 60.00 120.00 73 6840 1440 6615 1440 74 4 1 0 50 -1 0 12 0.0000 0 135 1170 1665 3375 Initial State\001 75 4 1 0 50 -1 0 12 0.0000 0 135 1170 3195 3375 Found Handler\001 76 4 1 0 50 -1 0 12 0.0000 0 165 1530 6570 3375 Handling Complete\001 77 4 2 0 50 -1 0 12 0.0000 0 135 720 1485 2385 handlers\001 78 4 1 0 50 -1 0 12 0.0000 0 135 900 4905 3375 Handler in\001 79 4 1 0 50 -1 0 12 0.0000 0 165 810 4905 3600 Try block\001 80 4 0 0 50 -1 0 12 0.0000 0 135 360 855 1665 head\001 81 4 0 0 50 -1 0 12 0.0000 4 120 810 2025 1485 execution\001 82 4 0 0 50 -1 0 12 0.0000 0 135 360 2385 2385 head\001 83 4 0 0 50 -1 0 12 0.0000 4 120 810 3645 1665 execution\001 84 4 0 0 50 -1 0 12 0.0000 0 135 360 4095 990 head\001 85 4 0 0 50 -1 0 12 0.0000 4 120 810 5310 810 execution\001 86 4 0 0 50 -1 0 12 0.0000 0 135 360 5715 1665 head\001 87 4 0 0 50 -1 0 12 0.0000 4 120 810 6885 1485 execution\001 -
doc/theses/andrew_beach_MMath/uw-ethesis-frontpgs.tex
rf95634e rb7fd9daf 129 129 \begin{center}\textbf{Abstract}\end{center} 130 130 131 This is the abstract. 131 The \CFA (Cforall) programming language is an evolutionary refinement of 132 the C programming language, adding modern programming features without 133 changing the programming paradigms of C. 134 One of these modern programming features is more powerful error handling 135 through the addition of an exception handling mechanism (EHM). 136 137 This thesis covers the design and implementation of the \CFA EHM, 138 along with a review of the other required \CFA features. 139 The EHM includes common features of termination exception handling, 140 which abandons and recovers from an operation, 141 and similar support for resumption exception handling, 142 which repairs and continues with an operation. 143 The design of both has been adapted to utilize other tools \CFA provides, 144 as well as fit with the assertion based interfaces of the language. 145 146 The EHM has been implemented into the \CFA compiler and run-time environment. 147 Although it has not yet been optimized, performance testing has shown it has 148 comparable performance to other EHMs, 149 which is sufficient for use in current \CFA programs. 132 150 133 151 \cleardoublepage … … 138 156 \begin{center}\textbf{Acknowledgements}\end{center} 139 157 140 I would like to thank all the little people who made this thesis possible. 158 As is tradition and his due, I would like to begin by thanking my 159 supervisor Peter Buhr. From accepting me in a first place, 160 to helping me run performance tests, I would not be here without him. 161 Also if there was an ``artist" field here he would be listed there as well, 162 he helped me a lot with the diagrams. 163 164 I would like to thank the readers 165 Gregor Richards and Yizhou Zhang 166 for their feedback and approval. 167 The presentation of the thesis has definitely been improved with their 168 feedback. 169 170 I also thank the entire Cforall Team who built the rest of the 171 \CFA compiler. From the existing features I used in my work, to the 172 internal tooling that makes further development easier and the optimizations 173 that make running tests pass by quickly. 174 This includes: Aaron Moss, Rob Schluntz, Thierry Delisle, Michael Brooks, 175 Mubeen Zulfieqar \& Fangren Yu. 176 177 And thank-you Henry Xue, the co-op student who 178 converted my macro implementation of exception declarations into 179 the compiler features presented in this thesis. 180 181 Finally I thank my family, who are still relieved I learned how to read. 182 141 183 \cleardoublepage 142 184 -
doc/theses/andrew_beach_MMath/uw-ethesis.bib
rf95634e rb7fd9daf 1 1 % Bibliography of key references for "LaTeX for Thesis and Large Documents" 2 2 % For use with BibTeX 3 % The online reference does not seem to be supported here. 3 4 4 @book{goossens.book, 5 author = "Michel Goossens and Frank Mittelbach and 6 Alexander Samarin", 7 title = "The \LaTeX\ Companion", 8 year = "1994", 9 publisher = "Addison-Wesley", 10 address = "Reading, Massachusetts" 5 @misc{Dice21, 6 author = {Dave Dice}, 7 year = 2021, 8 month = aug, 9 howpublished= {personal communication} 11 10 } 12 11 13 @book{knuth.book, 14 author = "Donald Knuth", 15 title = "The \TeX book", 16 year = "1986", 17 publisher = "Addison-Wesley", 18 address = "Reading, Massachusetts" 12 @misc{CforallExceptionBenchmarks, 13 contributer = {pabuhr@plg}, 14 key = {Cforall Exception Benchmarks}, 15 author = {{\textsf{C}{$\mathbf{\forall}$} Exception Benchmarks}}, 16 howpublished= {\href{https://github.com/cforall/ExceptionBenchmarks_SPE20}{https://\-github.com/\-cforall/\-ExceptionBenchmarks\_SPE20}}, 19 17 } 20 18 21 @book{lamport.book, 22 author = "Leslie Lamport", 23 title = "\LaTeX\ --- A Document Preparation System", 24 edition = "Second", 25 year = "1994", 26 publisher = "Addison-Wesley", 27 address = "Reading, Massachusetts" 19 % Could not get `#the-for-statement` to work. 20 @misc{PythonForLoop, 21 author={Python Software Foundation}, 22 key={Python Compound Statements}, 23 howpublished={\href{https://docs.python.org/3/reference/compound_stmts.html}{https://\-docs.python.org/\-3/\-reference/\-compound\_stmts.html}}, 24 addendum={Accessed 2021-08-30}, 28 25 } 26 27 % Again, I would like this to have `#StopIteration`. 28 @misc{PythonExceptions, 29 author={Python Software Foundation}, 30 key={Python Exceptions}, 31 howpublished={\href{https://docs.python.org/3/library/exceptions.html}{https://\-docs.python.org/\-3/\-library/\-exceptions.html}}, 32 addendum={Accessed 2021-08-30}, 33 } 34 35 @misc{CppHistory, 36 author={C++ Community}, 37 key={Cpp Reference History}, 38 howpublished={\href{https://en.cppreference.com/w/cpp/language/history}{https://\-en.cppreference.com/\-w/\-cpp/\-language/\-history}}, 39 addendum={Accessed 2021-08-30}, 40 } 41 42 @misc{CppExceptSpec, 43 author={C++ Community}, 44 key={Cpp Reference Exception Specification}, 45 howpublished={\href{https://en.cppreference.com/w/cpp/language/except_spec}{https://\-en.cppreference.com/\-w/\-cpp/\-language/\-except\_spec}}, 46 addendum={Accessed 2021-09-08}, 47 } 48 49 @misc{RustPanicMacro, 50 author={The Rust Team}, 51 key={Rust Panic Macro}, 52 howpublished={\href{https://doc.rust-lang.org/std/macro.panic.html}{https://\-doc.rust-lang.org/\-std/\-macro.panic.html}}, 53 addendum={Accessed 2021-08-31}, 54 } 55 56 @misc{RustPanicModule, 57 author={The Rust Team}, 58 key={Rust Panic Module}, 59 howpublished={\href{https://doc.rust-lang.org/std/panic/index.html}{https://\-doc.rust-lang.org/\-std/\-panic/\-index.html}}, 60 addendum={Accessed 2021-08-31}, 61 } 62 63 @manual{Go:2021, 64 keywords={Go programming language}, 65 author={Robert Griesemer and Rob Pike and Ken Thompson}, 66 title={{Go} Programming Language}, 67 organization={Google}, 68 year=2021, 69 note={\href{http://golang.org/ref/spec}{http://\-golang.org/\-ref/\-spec}}, 70 addendum={Accessed 2021-08-31}, 71 } -
doc/theses/andrew_beach_MMath/uw-ethesis.tex
rf95634e rb7fd9daf 210 210 \lstMakeShortInline@ 211 211 \lstset{language=CFA,style=cfacommon,basicstyle=\linespread{0.9}\tt} 212 % PAB causes problems with inline @=213 %\lstset{moredelim=**[is][\protect\color{red}]{@}{@}}214 212 % Annotations from Peter: 215 213 \newcommand{\PAB}[1]{{\color{blue}PAB: #1}} -
doc/theses/andrew_beach_MMath/vtable-layout.fig
rf95634e rb7fd9daf 8 8 -2 9 9 1200 2 10 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 111 1620 166512 10 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 13 11 3510 1890 3645 1755 … … 16 14 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 17 15 3645 1305 3645 1755 16 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 17 2115 1935 2250 1935 18 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 19 2250 1170 2115 1170 2115 2475 2250 2475 20 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 21 2250 1350 2115 1350 18 22 4 0 0 50 -1 0 12 0.0000 4 165 630 2295 1305 type_id\001 19 23 4 0 0 50 -1 0 12 0.0000 4 165 1170 2295 1500 parent_field0\001 -
doc/theses/mubeen_zulfiqar_MMath/allocator.tex
rf95634e rb7fd9daf 24 24 \end{itemize} 25 25 26 The new features added to uHeapLmmm (incl. @malloc _size@ routine)26 The new features added to uHeapLmmm (incl. @malloc\_size@ routine) 27 27 \CFA alloc interface with examples. 28 28 29 \begin{itemize} 29 30 \item … … 117 118 We added a few more features and routines to the allocator's C interface that can make the allocator more usable to the programmers. THese features will programmer more control on the dynamic memory allocation. 118 119 119 \subsubsection void * aalloc( size _t dim, size_t elemSize )120 \subsubsection void * aalloc( size\_t dim, size\_t elemSize ) 120 121 aalloc is an extension of malloc. It allows programmer to allocate a dynamic array of objects without calculating the total size of array explicitly. The only alternate of this routine in the other allocators is calloc but calloc also fills the dynamic memory with 0 which makes it slower for a programmer who only wants to dynamically allocate an array of objects without filling it with 0. 121 122 \paragraph{Usage} 122 123 aalloc takes two parameters. 124 123 125 \begin{itemize} 124 126 \item … … 129 131 It returns address of dynamic object allocatoed on heap that can contain dim number of objects of the size elemSize. On failure, it returns NULL pointer. 130 132 131 \subsubsection void * resize( void * oaddr, size _t size )133 \subsubsection void * resize( void * oaddr, size\_t size ) 132 134 resize is an extension of relloc. It allows programmer to reuse a cuurently allocated dynamic object with a new size requirement. Its alternate in the other allocators is realloc but relloc also copy the data in old object to the new object which makes it slower for the programmer who only wants to reuse an old dynamic object for a new size requirement but does not want to preserve the data in the old object to the new object. 133 135 \paragraph{Usage} 134 136 resize takes two parameters. 137 135 138 \begin{itemize} 136 139 \item … … 141 144 It returns an object that is of the size given but it does not preserve the data in the old object. On failure, it returns NULL pointer. 142 145 143 \subsubsection void * resize( void * oaddr, size _t nalign, size_t size )146 \subsubsection void * resize( void * oaddr, size\_t nalign, size\_t size ) 144 147 This resize is an extension of the above resize (FIX ME: cite above resize). In addition to resizing the size of of an old object, it can also realign the old object to a new alignment requirement. 145 148 \paragraph{Usage} 146 149 This resize takes three parameters. It takes an additional parameter of nalign as compared to the above resize (FIX ME: cite above resize). 150 147 151 \begin{itemize} 148 152 \item … … 155 159 It returns an object with the size and alignment given in the parameters. On failure, it returns a NULL pointer. 156 160 157 \subsubsection void * amemalign( size _t alignment, size_t dim, size_t elemSize )161 \subsubsection void * amemalign( size\_t alignment, size\_t dim, size\_t elemSize ) 158 162 amemalign is a hybrid of memalign and aalloc. It allows programmer to allocate an aligned dynamic array of objects without calculating the total size of the array explicitly. It frees the programmer from calculating the total size of the array. 159 163 \paragraph{Usage} 160 164 amemalign takes three parameters. 165 161 166 \begin{itemize} 162 167 \item … … 169 174 It returns a dynamic array of objects that has the capacity to contain dim number of objects of the size of elemSize. The returned dynamic array is aligned to the given alignment. On failure, it returns NULL pointer. 170 175 171 \subsubsection void * cmemalign( size _t alignment, size_t dim, size_t elemSize )176 \subsubsection void * cmemalign( size\_t alignment, size\_t dim, size\_t elemSize ) 172 177 cmemalign is a hybrid of amemalign and calloc. It allows programmer to allocate an aligned dynamic array of objects that is 0 filled. The current way to do this in other allocators is to allocate an aligned object with memalign and then fill it with 0 explicitly. This routine provides both features of aligning and 0 filling, implicitly. 173 178 \paragraph{Usage} 174 179 cmemalign takes three parameters. 180 175 181 \begin{itemize} 176 182 \item … … 183 189 It returns a dynamic array of objects that has the capacity to contain dim number of objects of the size of elemSize. The returned dynamic array is aligned to the given alignment and is 0 filled. On failure, it returns NULL pointer. 184 190 185 \subsubsection size_t malloc_alignment( void * addr ) 186 malloc_alignment returns the alignment of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required alignment. 187 \paragraph{Usage} 188 malloc_alignment takes one parameters. 191 \subsubsection size\_t malloc\_alignment( void * addr ) 192 malloc\_alignment returns the alignment of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required alignment. 193 \paragraph{Usage} 194 malloc\_alignment takes one parameters. 195 189 196 \begin{itemize} 190 197 \item 191 198 addr: the address of the currently allocated dynamic object. 192 199 \end{itemize} 193 malloc_alignment returns the alignment of the given dynamic object. On failure, it return the value of default alignment of the uHeapLmmm allocator. 194 195 \subsubsection bool malloc_zero_fill( void * addr ) 196 malloc_zero_fill returns whether a currently allocated dynamic object was initially zero filled at the time of allocation. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verifying the zero filled property of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was zero filled at the time of allocation. 197 \paragraph{Usage} 198 malloc_zero_fill takes one parameters. 200 malloc\_alignment returns the alignment of the given dynamic object. On failure, it return the value of default alignment of the uHeapLmmm allocator. 201 202 \subsubsection bool malloc\_zero\_fill( void * addr ) 203 malloc\_zero\_fill returns whether a currently allocated dynamic object was initially zero filled at the time of allocation. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verifying the zero filled property of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was zero filled at the time of allocation. 204 \paragraph{Usage} 205 malloc\_zero\_fill takes one parameters. 206 199 207 \begin{itemize} 200 208 \item 201 209 addr: the address of the currently allocated dynamic object. 202 210 \end{itemize} 203 malloc_zero_fill returns true if the dynamic object was initially zero filled and return false otherwise. On failure, it returns false. 204 205 \subsubsection size_t malloc_size( void * addr ) 206 malloc_size returns the allocation size of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required size. Its current alternate in the other allocators is malloc_usable_size. But, malloc_size is different from malloc_usable_size as malloc_usabe_size returns the total data capacity of dynamic object including the extra space at the end of the dynamic object. On the other hand, malloc_size returns the size that was given to the allocator at the allocation of the dynamic object. This size is updated when an object is realloced, resized, or passed through a similar allocator routine. 207 \paragraph{Usage} 208 malloc_size takes one parameters. 211 malloc\_zero\_fill returns true if the dynamic object was initially zero filled and return false otherwise. On failure, it returns false. 212 213 \subsubsection size\_t malloc\_size( void * addr ) 214 malloc\_size returns the allocation size of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required size. Its current alternate in the other allocators is malloc\_usable\_size. But, malloc\_size is different from malloc\_usable\_size as malloc\_usabe\_size returns the total data capacity of dynamic object including the extra space at the end of the dynamic object. On the other hand, malloc\_size returns the size that was given to the allocator at the allocation of the dynamic object. This size is updated when an object is realloced, resized, or passed through a similar allocator routine. 215 \paragraph{Usage} 216 malloc\_size takes one parameters. 217 209 218 \begin{itemize} 210 219 \item 211 220 addr: the address of the currently allocated dynamic object. 212 221 \end{itemize} 213 malloc _size returns the allocation size of the given dynamic object. On failure, it return zero.214 215 \subsubsection void * realloc( void * oaddr, size _t nalign, size_t size )222 malloc\_size returns the allocation size of the given dynamic object. On failure, it return zero. 223 224 \subsubsection void * realloc( void * oaddr, size\_t nalign, size\_t size ) 216 225 This realloc is an extension of the default realloc (FIX ME: cite default realloc). In addition to reallocating an old object and preserving the data in old object, it can also realign the old object to a new alignment requirement. 217 226 \paragraph{Usage} 218 227 This realloc takes three parameters. It takes an additional parameter of nalign as compared to the default realloc. 228 219 229 \begin{itemize} 220 230 \item … … 237 247 It returns a dynamic object of the size of type T. On failure, it return NULL pointer. 238 248 239 \subsubsection T * aalloc( size _t dim )249 \subsubsection T * aalloc( size\_t dim ) 240 250 This aalloc is a simplified polymorphic form of above aalloc (FIX ME: cite aalloc). It takes one parameter as compared to the above aalloc that takes two parameters. 241 251 \paragraph{Usage} 242 252 aalloc takes one parameters. 253 243 254 \begin{itemize} 244 255 \item … … 247 258 It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. On failure, it return NULL pointer. 248 259 249 \subsubsection T * calloc( size _t dim )260 \subsubsection T * calloc( size\_t dim ) 250 261 This calloc is a simplified polymorphic form of defualt calloc (FIX ME: cite calloc). It takes one parameter as compared to the default calloc that takes two parameters. 251 262 \paragraph{Usage} 252 263 This calloc takes one parameter. 264 253 265 \begin{itemize} 254 266 \item … … 257 269 It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. On failure, it return NULL pointer. 258 270 259 \subsubsection T * resize( T * ptr, size _t size )271 \subsubsection T * resize( T * ptr, size\_t size ) 260 272 This resize is a simplified polymorphic form of above resize (FIX ME: cite resize with alignment). It takes two parameters as compared to the above resize that takes three parameters. It frees the programmer from explicitly mentioning the alignment of the allocation as CFA provides gives allocator the liberty to get the alignment of the returned type. 261 273 \paragraph{Usage} 262 274 This resize takes two parameters. 275 263 276 \begin{itemize} 264 277 \item … … 269 282 It returns a dynamic object of the size given in paramters. The returned object is aligned to the alignemtn of type T. On failure, it return NULL pointer. 270 283 271 \subsubsection T * realloc( T * ptr, size _t size )284 \subsubsection T * realloc( T * ptr, size\_t size ) 272 285 This realloc is a simplified polymorphic form of defualt realloc (FIX ME: cite realloc with align). It takes two parameters as compared to the above realloc that takes three parameters. It frees the programmer from explicitly mentioning the alignment of the allocation as CFA provides gives allocator the liberty to get the alignment of the returned type. 273 286 \paragraph{Usage} 274 287 This realloc takes two parameters. 288 275 289 \begin{itemize} 276 290 \item … … 281 295 It returns a dynamic object of the size given in paramters that preserves the data in the given object. The returned object is aligned to the alignemtn of type T. On failure, it return NULL pointer. 282 296 283 \subsubsection T * memalign( size _t align )297 \subsubsection T * memalign( size\_t align ) 284 298 This memalign is a simplified polymorphic form of defualt memalign (FIX ME: cite memalign). It takes one parameters as compared to the default memalign that takes two parameters. 285 299 \paragraph{Usage} 286 300 memalign takes one parameters. 301 287 302 \begin{itemize} 288 303 \item … … 291 306 It returns a dynamic object of the size of type T that is aligned to given parameter align. On failure, it return NULL pointer. 292 307 293 \subsubsection T * amemalign( size _t align, size_t dim )308 \subsubsection T * amemalign( size\_t align, size\_t dim ) 294 309 This amemalign is a simplified polymorphic form of above amemalign (FIX ME: cite amemalign). It takes two parameter as compared to the above amemalign that takes three parameters. 295 310 \paragraph{Usage} 296 311 amemalign takes two parameters. 312 297 313 \begin{itemize} 298 314 \item … … 303 319 It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. The returned object is aligned to the given parameter align. On failure, it return NULL pointer. 304 320 305 \subsubsection T * cmemalign( size _t align, size_t dim )321 \subsubsection T * cmemalign( size\_t align, size\_t dim ) 306 322 This cmemalign is a simplified polymorphic form of above cmemalign (FIX ME: cite cmemalign). It takes two parameter as compared to the above cmemalign that takes three parameters. 307 323 \paragraph{Usage} 308 324 cmemalign takes two parameters. 325 309 326 \begin{itemize} 310 327 \item … … 315 332 It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. The returned object is aligned to the given parameter align and is zero filled. On failure, it return NULL pointer. 316 333 317 \subsubsection T * aligned_alloc( size_t align ) 318 This aligned_alloc is a simplified polymorphic form of defualt aligned_alloc (FIX ME: cite aligned_alloc). It takes one parameter as compared to the default aligned_alloc that takes two parameters. 319 \paragraph{Usage} 320 This aligned_alloc takes one parameter. 334 \subsubsection T * aligned\_alloc( size\_t align ) 335 This aligned\_alloc is a simplified polymorphic form of defualt aligned\_alloc (FIX ME: cite aligned\_alloc). It takes one parameter as compared to the default aligned\_alloc that takes two parameters. 336 \paragraph{Usage} 337 This aligned\_alloc takes one parameter. 338 321 339 \begin{itemize} 322 340 \item … … 325 343 It returns a dynamic object of the size of type T that is aligned to the given parameter. On failure, it return NULL pointer. 326 344 327 \subsubsection int posix_memalign( T ** ptr, size_t align ) 328 This posix_memalign is a simplified polymorphic form of defualt posix_memalign (FIX ME: cite posix_memalign). It takes two parameters as compared to the default posix_memalign that takes three parameters. 329 \paragraph{Usage} 330 This posix_memalign takes two parameter. 345 \subsubsection int posix\_memalign( T ** ptr, size\_t align ) 346 This posix\_memalign is a simplified polymorphic form of defualt posix\_memalign (FIX ME: cite posix\_memalign). It takes two parameters as compared to the default posix\_memalign that takes three parameters. 347 \paragraph{Usage} 348 This posix\_memalign takes two parameter. 349 331 350 \begin{itemize} 332 351 \item … … 335 354 align: required alignment of the dynamic object. 336 355 \end{itemize} 356 337 357 It stores address of the dynamic object of the size of type T in given parameter ptr. This object is aligned to the given parameter. On failure, it return NULL pointer. 338 358 … … 349 369 It returns a dynamic object of the size that is calcutaed by rouding the size of type T. The returned object is also aligned to the page size. On failure, it return NULL pointer. 350 370 351 \subsection {Alloc Interface}371 \subsection Alloc Interface 352 372 In addition to improve allocator interface both for CFA and our standalone allocator uHeapLmmm in C. We also added a new alloc interface in CFA that increases usability of dynamic memory allocation. 353 373 This interface helps programmers in three major ways. 374 354 375 \begin{itemize} 355 376 \item … … 371 392 This is the only parameter in the alloc routine that has a fixed-position and it is also the only parameter that does not use a backtick function. It has to be passed at the first position to alloc call in-case of an array allocation of objects of type T. 372 393 It represents the required number of members in the array allocation as in CFA's aalloc (FIX ME: cite aalloc). 373 This parameter should be of type size _t.394 This parameter should be of type size\_t. 374 395 375 396 Example: int a = alloc( 5 ) … … 377 398 378 399 \paragraph{Align} 379 This parameter is position-free and uses a backtick routine align (`align). The parameter passed with `align should be of type size _t. If the alignment parameter is not a power of two or is less than the default alignment of the allocator (that can be found out using routine libAlign in CFA) then the passed alignment parameter will be rejected and the default alignment will be used.400 This parameter is position-free and uses a backtick routine align (`align). The parameter passed with `align should be of type size\_t. If the alignment parameter is not a power of two or is less than the default alignment of the allocator (that can be found out using routine libAlign in CFA) then the passed alignment parameter will be rejected and the default alignment will be used. 380 401 381 402 Example: int b = alloc( 5 , 64`align ) … … 385 406 This parameter is position-free and uses a backtick routine fill (`fill). In case of realloc, only the extra space after copying the data in the old object will be filled with given parameter. 386 407 Three types of parameters can be passed using `fill. 408 387 409 \begin{itemize} 388 410 \item -
doc/theses/mubeen_zulfiqar_MMath/background.tex
rf95634e rb7fd9daf 23 23 ==================== 24 24 25 \cite{Wasik08} 25 \section{Background} 26 27 % FIXME: cite wasik 28 \cite{wasik.thesis} 29 30 \subsection{Memory Allocation} 31 With dynamic allocation being an important feature of C, there are many standalone memory allocators that have been designed for different purposes. For this thesis, we chose 7 of the most popular and widely used memory allocators. 32 33 \paragraph{dlmalloc} 34 dlmalloc (FIX ME: cite allocator) is a thread-safe allocator that is single threaded and single heap. dlmalloc maintains free-lists of different sizes to store freed dynamic memory. (FIX ME: cite wasik) 35 36 \paragraph{hoard} 37 Hoard (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and using a heap layer framework. It has per-thred heaps that have thread-local free-lists, and a gloabl shared heap. (FIX ME: cite wasik) 38 39 \paragraph{jemalloc} 40 jemalloc (FIX ME: cite allocator) is a thread-safe allocator that uses multiple arenas. Each thread is assigned an arena. Each arena has chunks that contain contagious memory regions of same size. An arena has multiple chunks that contain regions of multiple sizes. 41 42 \paragraph{ptmalloc} 43 ptmalloc (FIX ME: cite allocator) is a modification of dlmalloc. It is a thread-safe multi-threaded memory allocator that uses multiple heaps. ptmalloc heap has similar design to dlmalloc's heap. 44 45 \paragraph{rpmalloc} 46 rpmalloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses per-thread heap. Each heap has multiple size-classes and each size-calss contains memory regions of the relevant size. 47 48 \paragraph{tbb malloc} 49 tbb malloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses private heap for each thread. Each private-heap has multiple bins of different sizes. Each bin contains free regions of the same size. 50 51 \paragraph{tc malloc} 52 tcmalloc (FIX ME: cite allocator) is a thread-safe allocator. It uses per-thread cache to store free objects that prevents contention on shared resources in multi-threaded application. A central free-list is used to refill per-thread cache when it gets empty. 53 54 \subsection{Benchmarks} 55 There are multiple benchmarks that are built individually and evaluate different aspects of a memory allocator. But, there is not standard set of benchamrks that can be used to evaluate multiple aspects of memory allocators. 56 57 \paragraph{threadtest} 58 (FIX ME: cite benchmark and hoard) Each thread repeatedly allocates and then deallocates 100,000 objects. Runtime of the benchmark evaluates its efficiency. 59 60 \paragraph{shbench} 61 (FIX ME: cite benchmark and hoard) Each thread allocates and randomly frees a number of random-sized objects. It is a stress test that also uses runtime to determine efficiency of the allocator. 62 63 \paragraph{larson} 64 (FIX ME: cite benchmark and hoard) Larson simulates a server environment. Multiple threads are created where each thread allocator and free a number of objects within a size range. Some objects are passed from threads to the child threads to free. It caluculates memory operations per second as an indicator of memory allocator's performance. -
doc/theses/mubeen_zulfiqar_MMath/benchmarks.tex
rf95634e rb7fd9daf 149 149 *** FIX ME: Insert a figure of above benchmark with description 150 150 151 \paragr pah{Relevant Knobs}151 \paragraph{Relevant Knobs} 152 152 *** FIX ME: Insert Relevant Knobs 153 153 -
doc/theses/mubeen_zulfiqar_MMath/intro.tex
rf95634e rb7fd9daf 47 47 \begin{itemize} 48 48 \item 49 aligned _alloc49 aligned\_alloc 50 50 \item 51 malloc _usable_size51 malloc\_usable\_size 52 52 \item 53 53 memalign 54 54 \item 55 posix _memalign55 posix\_memalign 56 56 \item 57 57 pvalloc … … 61 61 62 62 With the rise of concurrent applications, memory allocators should be able to fulfill dynamic memory requests from multiple threads in parallel without causing contention on shared resources. There needs to be a set of a standard benchmarks that can be used to evaluate an allocator's performance in different scenerios. 63 64 \section{Background}65 66 \subsection{Memory Allocation}67 With dynamic allocation being an important feature of C, there are many standalone memory allocators that have been designed for different purposes. For this thesis, we chose 7 of the most popular and widely used memory allocators.68 69 \paragraph{dlmalloc}70 dlmalloc (FIX ME: cite allocator) is a thread-safe allocator that is single threaded and single heap. dlmalloc maintains free-lists of different sizes to store freed dynamic memory. (FIX ME: cite wasik)71 72 \paragraph{hoard}73 Hoard (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and using a heap layer framework. It has per-thred heaps that have thread-local free-lists, and a gloabl shared heap. (FIX ME: cite wasik)74 75 \paragraph{jemalloc}76 jemalloc (FIX ME: cite allocator) is a thread-safe allocator that uses multiple arenas. Each thread is assigned an arena. Each arena has chunks that contain contagious memory regions of same size. An arena has multiple chunks that contain regions of multiple sizes.77 78 \paragraph{ptmalloc}79 ptmalloc (FIX ME: cite allocator) is a modification of dlmalloc. It is a thread-safe multi-threaded memory allocator that uses multiple heaps. ptmalloc heap has similar design to dlmalloc's heap.80 81 \paragraph{rpmalloc}82 rpmalloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses per-thread heap. Each heap has multiple size-classes and each size-calss contains memory regions of the relevant size.83 84 \paragraph{tbb malloc}85 tbb malloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses private heap for each thread. Each private-heap has multiple bins of different sizes. Each bin contains free regions of the same size.86 87 \paragraph{tc malloc}88 tcmalloc (FIX ME: cite allocator) is a thread-safe allocator. It uses per-thread cache to store free objects that prevents contention on shared resources in multi-threaded application. A central free-list is used to refill per-thread cache when it gets empty.89 90 \subsection{Benchmarks}91 There are multiple benchmarks that are built individually and evaluate different aspects of a memory allocator. But, there is not standard set of benchamrks that can be used to evaluate multiple aspects of memory allocators.92 93 \paragraph{threadtest}94 (FIX ME: cite benchmark and hoard) Each thread repeatedly allocates and then deallocates 100,000 objects. Runtime of the benchmark evaluates its efficiency.95 96 \paragraph{shbench}97 (FIX ME: cite benchmark and hoard) Each thread allocates and randomly frees a number of random-sized objects. It is a stress test that also uses runtime to determine efficiency of the allocator.98 99 \paragraph{larson}100 (FIX ME: cite benchmark and hoard) Larson simulates a server environment. Multiple threads are created where each thread allocator and free a number of objects within a size range. Some objects are passed from threads to the child threads to free. It caluculates memory operations per second as an indicator of memory allocator's performance.101 63 102 64 \section{Research Objectives} -
doc/theses/mubeen_zulfiqar_MMath/performance.tex
rf95634e rb7fd9daf 44 44 tc & & \\ 45 45 \end{tabularx} 46 (FIX ME: complete table) 46 47 %(FIX ME: complete table) 47 48 48 49 \section{Experiment Environment} -
doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.bib
rf95634e rb7fd9daf 27 27 address = "Reading, Massachusetts" 28 28 } 29 30 @article{wasik.thesis, 31 author = "Ayelet Wasik", 32 title = "Features of A Multi-Threaded Memory Alloator", 33 publisher = "University of Waterloo", 34 year = "2008" 35 } -
doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.tex
rf95634e rb7fd9daf 84 84 \usepackage{graphicx} 85 85 \usepackage{comment} % Removes large sections of the document. 86 \usepackage{tabularx} 86 87 87 88 % Hyperlinks make it very easy to navigate an electronic document. … … 191 192 % Tip: Putting each sentence on a new line is a way to simplify later editing. 192 193 %---------------------------------------------------------------------- 194 \begin{sloppypar} 195 193 196 \input{intro} 194 197 \input{background} … … 197 200 \input{performance} 198 201 \input{conclusion} 202 203 \end{sloppypar} 199 204 200 205 %---------------------------------------------------------------------- -
doc/theses/thierry_delisle_PhD/.gitignore
rf95634e rb7fd9daf 13 13 comp_II/presentation.pdf 14 14 15 seminars/build/ 16 seminars/img/*.fig.bak 17 seminars/*.pdf 18 15 19 thesis/build/ 16 20 thesis/fig/*.fig.bak -
doc/theses/thierry_delisle_PhD/thesis/Makefile
rf95634e rb7fd9daf 20 20 practice \ 21 21 io \ 22 eval_micro \ 23 eval_macro \ 22 24 }} 23 25 … … 35 37 pivot_ring \ 36 38 system \ 39 cycle \ 37 40 } 38 41 -
doc/theses/thierry_delisle_PhD/thesis/thesis.tex
rf95634e rb7fd9daf 1 1 %====================================================================== 2 % University of Waterloo Thesis Template for LaTeX 3 % Last Updated November, 2020 4 % by Stephen Carr, IST Client Services, 2 % University of Waterloo Thesis Template for LaTeX 3 % Last Updated November, 2020 4 % by Stephen Carr, IST Client Services, 5 5 % University of Waterloo, 200 University Ave. W., Waterloo, Ontario, Canada 6 6 % FOR ASSISTANCE, please send mail to request@uwaterloo.ca … … 15 15 % Some important notes on using this template and making it your own... 16 16 17 % The University of Waterloo has required electronic thesis submission since October 2006. 17 % The University of Waterloo has required electronic thesis submission since October 2006. 18 18 % See the uWaterloo thesis regulations at 19 19 % https://uwaterloo.ca/graduate-studies/thesis. 20 20 % This thesis template is geared towards generating a PDF version optimized for viewing on an electronic display, including hyperlinks within the PDF. 21 21 22 % DON'T FORGET TO ADD YOUR OWN NAME AND TITLE in the "hyperref" package configuration below. 22 % DON'T FORGET TO ADD YOUR OWN NAME AND TITLE in the "hyperref" package configuration below. 23 23 % THIS INFORMATION GETS EMBEDDED IN THE PDF FINAL PDF DOCUMENT. 24 24 % You can view the information if you view properties of the PDF document. 25 25 26 % Many faculties/departments also require one or more printed copies. 27 % This template attempts to satisfy both types of output. 26 % Many faculties/departments also require one or more printed copies. 27 % This template attempts to satisfy both types of output. 28 28 % See additional notes below. 29 29 % It is based on the standard "book" document class which provides all necessary sectioning structures and allows multi-part theses. … … 32 32 33 33 % For people who prefer to install their own LaTeX distributions on their own computers, and process the source files manually, the following notes provide the sequence of tasks: 34 34 35 35 % E.g. to process a thesis called "mythesis.tex" based on this template, run: 36 36 37 37 % pdflatex mythesis -- first pass of the pdflatex processor 38 38 % bibtex mythesis -- generates bibliography from .bib data file(s) 39 % makeindex -- should be run only if an index is used 39 % makeindex -- should be run only if an index is used 40 40 % pdflatex mythesis -- fixes numbering in cross-references, bibliographic references, glossaries, index, etc. 41 41 % pdflatex mythesis -- it takes a couple of passes to completely process all cross-references 42 42 43 43 % If you use the recommended LaTeX editor, Texmaker, you would open the mythesis.tex file, then click the PDFLaTeX button. Then run BibTeX (under the Tools menu). 44 % Then click the PDFLaTeX button two more times. 44 % Then click the PDFLaTeX button two more times. 45 45 % If you have an index as well,you'll need to run MakeIndex from the Tools menu as well, before running pdflatex 46 46 % the last two times. … … 51 51 % Tip: Photographs should be cropped and compressed so as not to be too large. 52 52 53 % To create a PDF output that is optimized for double-sided printing: 53 % To create a PDF output that is optimized for double-sided printing: 54 54 % 1) comment-out the \documentclass statement in the preamble below, and un-comment the second \documentclass line. 55 55 % 2) change the value assigned below to the boolean variable "PrintVersion" from " false" to "true". … … 67 67 % If you have to, it's easier to make changes to nomenclature once here than in a million places throughout your thesis! 68 68 \newcommand{\package}[1]{\textbf{#1}} % package names in bold text 69 \newcommand{\cmmd}[1]{\textbackslash\texttt{#1}} % command name in tt font 69 \newcommand{\cmmd}[1]{\textbackslash\texttt{#1}} % command name in tt font 70 70 \newcommand{\href}[1]{#1} % does nothing, but defines the command so the print-optimized version will ignore \href tags (redefined by hyperref pkg). 71 71 %\newcommand{\texorpdfstring}[2]{#1} % does nothing, but defines the command … … 235 235 \part{Evaluation} 236 236 \label{Evaluation} 237 \chapter{Theoretical and Existance Proofs}238 \ chapter{Micro-Benchmarks}239 \ chapter{Larger-Scale applications}237 % \chapter{Theoretical and Existance Proofs} 238 \input{text/eval_micro.tex} 239 \input{text/eval_macro.tex} 240 240 \part{Conclusion \& Annexes} 241 241 -
doc/user/user.tex
rf95634e rb7fd9daf 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon May 31 09:03:34202114 %% Update Count : 50 7113 %% Last Modified On : Sun Oct 10 12:45:00 2021 14 %% Update Count : 5095 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 4444 4444 \CFA provides a fine-grained solution where a \Index{recursive lock} is acquired and released indirectly via a manipulator ©acquire© or instantiating an \Index{RAII} type specific for the kind of stream: ©osacquire©\index{ostream@©ostream©!osacquire@©osacquire©} for output streams and ©isacquire©\index{isacquire@©isacquire©}\index{istream@©istream©!isacquire@©isacquire©} for input streams. 4445 4445 4446 The common usage is manipulator ©acquire©\index{ostream@©ostream©!acquire@©acquire©} to lock a stream during a single cascaded I/O expression, with the manipulator appearing as the first item in a cascade list, \eg:4447 \begin{cfa} 4448 $\emph{thread\(_1\)}$ : sout | ®acquire® | "abc " | "def "; // manipulator4449 $\emph{thread\(_2\)}$ : sout | ®acquire®| "uvw " | "xyz ";4446 The common usage is the short form of the mutex statement\index{ostream@©ostream©!mutex@©mutex©} to lock a stream during a single cascaded I/O expression, \eg: 4447 \begin{cfa} 4448 $\emph{thread\(_1\)}$ : ®mutex()® sout | "abc " | "def "; 4449 $\emph{thread\(_2\)}$ : ®mutex()® sout | "uvw " | "xyz "; 4450 4450 \end{cfa} 4451 4451 Now, the order of the thread execution is still non-deterministic, but the output is constrained to two possible lines in either order. … … 4466 4466 In summary, the stream lock is acquired by the ©acquire© manipulator and implicitly released at the end of the cascaded I/O expression ensuring all operations in the expression occur atomically. 4467 4467 4468 To lock a stream across multiple I/O operations, an object of type ©osacquire© or ©isacquire© is declared to implicitly acquire/release the stream lock providing mutual exclusion for the object's duration, \eg: 4469 \begin{cfa} 4470 { // acquire sout for block duration 4471 ®osacquire® acq = { sout }; $\C{// named stream locker}$ 4468 To lock a stream across multiple I/O operations, he long form of the mutex statement is used, \eg: 4469 \begin{cfa} 4470 ®mutex( sout )® { 4472 4471 sout | 1; 4473 sout | ®acquire® | 2 | 3; $\C{// unnecessary, but ok to acquire and release again}$4472 ®mutex() sout® | 2 | 3; $\C{// unnecessary, but ok because of recursive lock}$ 4474 4473 sout | 4; 4475 } // implicitly release the lock when "acq" is deallocated4476 \end{cfa} 4477 Note, the unnecessary © acquire© manipulatorworks because the recursive stream-lock can be acquired/released multiple times by the owner thread.4474 } // implicitly release sout lock 4475 \end{cfa} 4476 Note, the unnecessary ©mutex© in the middle of the mutex statement, works because the recursive stream-lock can be acquired/released multiple times by the owner thread. 4478 4477 Hence, calls to functions that also acquire a stream lock for their output do not result in \Index{deadlock}. 4479 4478 4480 4479 The previous values written by threads 1 and 2 can be read in concurrently: 4481 4480 \begin{cfa} 4482 { // acquire sin lock for block duration 4483 ®isacquire acq = { sin };® $\C{// named stream locker}$ 4481 ®mutex( sin )® { 4484 4482 int x, y, z, w; 4485 4483 sin | x; 4486 sin | ®acquire® | y | z; $\C{// unnecessary, but ok to acquire and release again}$4484 ®mutex() sin® | y | z; $\C{// unnecessary, but ok because of recursive lock}$ 4487 4485 sin | w; 4488 } // implicitly release the lock when "acq" is deallocated4486 } // implicitly release sin lock 4489 4487 \end{cfa} 4490 4488 Again, the order of the reading threads is non-deterministic. … … 4493 4491 \Textbf{WARNING:} The general problem of \Index{nested locking} can occur if routines are called in an I/O sequence that block, \eg: 4494 4492 \begin{cfa} 4495 sout | ®acquire® | "data:" | rtn( mon ); $\C{// mutex call on monitor}$4493 ®mutex() sout® | "data:" | rtn( mon ); $\C{// mutex call on monitor}$ 4496 4494 \end{cfa} 4497 4495 If the thread executing the I/O expression blocks in the monitor with the ©sout© lock, other threads writing to ©sout© also block until the thread holding the lock is unblocked and releases it. … … 4500 4498 \begin{cfa} 4501 4499 int ®data® = rtn( mon ); 4502 sout | acquire| "data:" | ®data®;4500 mutex() sout | "data:" | ®data®; 4503 4501 \end{cfa} 4504 4502 … … 4506 4504 \section{String Stream} 4507 4505 4508 All the stream formatting capabilities are available to format text to/from a C string rather than to a stream file. 4509 \VRef[Figure]{f:StringStreamProcessing} shows writing (output) and reading (input) from a C string. 4506 The stream types ©ostrstream© and ©istrstream© provide all the stream formatting capabilities to/from a C string rather than a stream file. 4507 \VRef[Figure]{f:StringStreamProcessing} shows writing (output) to and reading (input) from a C string. 4508 The only string stream operations different from a file stream are: 4509 \begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt] 4510 \item 4511 constructors to create a stream that writes to a write buffer (©ostrstream©) of ©size©, or reads from a read buffer (©istrstream©) containing a C string terminated with ©'\0'©. 4512 \begin{cfa} 4513 void ?{}( ostrstream &, char buf[], size_t size ); 4514 void ?{}( istrstream & is, char buf[] ); 4515 \end{cfa} 4516 \item 4517 \Indexc{write} (©ostrstream© only) writes all the buffered characters to the specified stream (©stdout© default). 4518 \begin{cfa} 4519 ostrstream & write( ostrstream & os, FILE * stream = stdout ); 4520 \end{cfa} 4521 There is no ©read© for ©istrstream©. 4522 \end{itemize} 4523 4510 4524 \begin{figure} 4511 4525 \begin{cfa} … … 4520 4534 double x = 12345678.9, y = 98765.4321e-11; 4521 4535 4522 osstr | i | hex(j) | wd(10, k) | sci(x) | unit(eng(y)) ; $\C{// same lines of output}$4523 write( osstr ); 4524 printf( "%s", buf ); 4525 sout | i | hex(j) | wd(10, k) | sci(x) | unit(eng(y)) ;4526 4527 char buf2[] = "12 14 15 3.5 7e4 "; $\C{// input buffer}$4536 osstr | i | hex(j) | wd(10, k) | sci(x) | unit(eng(y)) | "abc"; 4537 write( osstr ); $\C{// write string to stdout}$ 4538 printf( "%s", buf ); $\C{// same lines of output}$ 4539 sout | i | hex(j) | wd(10, k) | sci(x) | unit(eng(y)) | "abc"; 4540 4541 char buf2[] = "12 14 15 3.5 7e4 abc"; $\C{// input buffer}$ 4528 4542 ®istrstream isstr = { buf2 };® 4529 isstr | i | j | k | x | y; 4530 sout | i | j | k | x | y; 4531 } 4543 char s[10]; 4544 isstr | i | j | k | x | y | s; 4545 sout | i | j | k | x | y | s; 4546 } 4547 4548 3 0x5 7 1.234568e+07 987.654n abc 4549 3 0x5 7 1.234568e+07 987.654n abc 4550 3 0x5 7 1.234568e+07 987.654n abc 4551 12 14 15 3.5 70000. abc 4532 4552 \end{cfa} 4533 4553 \caption{String Stream Processing} 4534 4554 \label{f:StringStreamProcessing} 4535 4555 \end{figure} 4536 4537 \VRef[Figure]{f:StringStreamFunctions} shows the string stream operations.4538 \begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]4539 \item4540 \Indexc{write} (©ostrstream© only) writes all the buffered characters to the specified stream (©stdout© default).4541 \end{itemize}4542 The constructor functions:4543 \begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]4544 \item4545 create a bound stream to a write buffer (©ostrstream©) of ©size© or a read buffer (©istrstream©) containing a C string terminated with ©'\0'©.4546 \end{itemize}4547 4548 \begin{figure}4549 \begin{cfa}4550 // *********************************** ostrstream ***********************************4551 4552 ostrstream & write( ostrstream & os, FILE * stream = stdout );4553 4554 void ?{}( ostrstream &, char buf[], size_t size );4555 4556 // *********************************** istrstream ***********************************4557 4558 void ?{}( istrstream & is, char buf[] );4559 \end{cfa}4560 \caption{String Stream Functions}4561 \label{f:StringStreamFunctions}4562 \end{figure}4563 4564 4556 4565 4557 \begin{comment} -
libcfa/prelude/bootloader.cf
rf95634e rb7fd9daf 3 3 char ** cfa_args_argv; 4 4 char ** cfa_args_envp; 5 int cfa_main_returned = 0;5 __attribute__((weak)) extern int cfa_main_returned; 6 6 7 7 int main(int argc, char* argv[], char* envp[]) { … … 10 10 cfa_args_envp = envp; 11 11 int ret = invoke_main(argc, argv, envp); 12 cfa_main_returned = 1;12 if(&cfa_main_returned) cfa_main_returned = 1; 13 13 return ret; 14 14 } -
libcfa/prelude/builtins.c
rf95634e rb7fd9daf 10 10 // Created On : Fri Jul 21 16:21:03 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jul 21 13:31:34 202113 // Update Count : 1 2912 // Last Modified On : Sat Aug 14 08:45:54 2021 13 // Update Count : 133 14 14 // 15 15 … … 107 107 #endif // __SIZEOF_INT128__ 108 108 109 // for-control index constraints 110 // forall( T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); T ?+=?( T &, T ); T ?-=?( T &, T ); int ?<?( T, T ); } ) 111 // static inline T __for_control_index_constraints__( T t ) { return t; } 112 109 113 // exponentiation operator implementation 110 114 -
libcfa/src/Makefile.am
rf95634e rb7fd9daf 48 48 math.hfa \ 49 49 time_t.hfa \ 50 bits/algorithm.hfa \ 50 51 bits/align.hfa \ 51 52 bits/containers.hfa \ … … 77 78 memory.hfa \ 78 79 parseargs.hfa \ 80 parseconfig.hfa \ 79 81 rational.hfa \ 80 82 stdlib.hfa \ … … 85 87 containers/pair.hfa \ 86 88 containers/result.hfa \ 89 containers/string.hfa \ 90 containers/string_res.hfa \ 87 91 containers/vector.hfa \ 88 92 device/cpu.hfa … … 90 94 libsrc = ${inst_headers_src} ${inst_headers_src:.hfa=.cfa} \ 91 95 assert.cfa \ 92 bits/algorithm.hfa \93 96 bits/debug.cfa \ 94 97 exception.c \ … … 106 109 concurrency/invoke.h \ 107 110 concurrency/future.hfa \ 108 concurrency/kernel/fwd.hfa 111 concurrency/kernel/fwd.hfa \ 112 concurrency/mutex_stmt.hfa 109 113 110 114 inst_thread_headers_src = \ … … 192 196 $(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@} 193 197 198 concurrency/io/call.cfa: $(srcdir)/concurrency/io/call.cfa.in 199 ${AM_V_GEN}python3 $< > $@ 200 194 201 #---------------------------------------------------------------------------------------------------------------- 195 202 libcfa_la_SOURCES = ${libsrc} -
libcfa/src/concurrency/clib/cfathread.cfa
rf95634e rb7fd9daf 14 14 // 15 15 16 // #define EPOLL_FOR_SOCKETS 17 16 18 #include "fstream.hfa" 17 19 #include "locks.hfa" … … 23 25 #include "cfathread.h" 24 26 27 extern "C" { 28 #include <string.h> 29 #include <errno.h> 30 } 31 25 32 extern void ?{}(processor &, const char[], cluster &, thread$ *); 26 33 extern "C" { 27 34 extern void __cfactx_invoke_thread(void (*main)(void *), void * this); 35 extern int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); 28 36 } 29 37 30 38 extern Time __kernel_get_time(); 39 extern unsigned register_proc_id( void ); 31 40 32 41 //================================================================================ 33 // Thread run y the C Interface 42 // Epoll support for sockets 43 44 #if defined(EPOLL_FOR_SOCKETS) 45 extern "C" { 46 #include <sys/epoll.h> 47 #include <sys/resource.h> 48 } 49 50 static pthread_t master_poller; 51 static int master_epollfd = 0; 52 static size_t poller_cnt = 0; 53 static int * poller_fds = 0p; 54 static struct leaf_poller * pollers = 0p; 55 56 struct __attribute__((aligned)) fd_info_t { 57 int pollid; 58 size_t rearms; 59 }; 60 rlim_t fd_limit = 0; 61 static fd_info_t * volatile * fd_map = 0p; 62 63 void * master_epoll( __attribute__((unused)) void * args ) { 64 unsigned id = register_proc_id(); 65 66 enum { MAX_EVENTS = 5 }; 67 struct epoll_event events[MAX_EVENTS]; 68 for() { 69 int ret = epoll_wait(master_epollfd, events, MAX_EVENTS, -1); 70 if ( ret < 0 ) { 71 abort | "Master epoll error: " | strerror(errno); 72 } 73 74 for(i; ret) { 75 thread$ * thrd = (thread$ *)events[i].data.u64; 76 unpark( thrd ); 77 } 78 } 79 80 return 0p; 81 } 82 83 static inline int epoll_rearm(int epollfd, int fd, uint32_t event) { 84 struct epoll_event eevent; 85 eevent.events = event | EPOLLET | EPOLLONESHOT; 86 eevent.data.u64 = (uint64_t)active_thread(); 87 88 if(0 != epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &eevent)) 89 { 90 if(errno == ENOENT) return -1; 91 abort | acquire | "epoll" | epollfd | "ctl rearm" | fd | "error: " | errno | strerror(errno); 92 } 93 94 park(); 95 return 0; 96 } 97 98 thread leaf_poller { 99 int epollfd; 100 }; 101 102 void ?{}(leaf_poller & this, int fd) { this.epollfd = fd; } 103 104 void main(leaf_poller & this) { 105 enum { MAX_EVENTS = 1024 }; 106 struct epoll_event events[MAX_EVENTS]; 107 const int max_retries = 5; 108 int retries = max_retries; 109 110 struct epoll_event event; 111 event.events = EPOLLIN | EPOLLET | EPOLLONESHOT; 112 event.data.u64 = (uint64_t)&(thread&)this; 113 114 if(0 != epoll_ctl(master_epollfd, EPOLL_CTL_ADD, this.epollfd, &event)) 115 { 116 abort | "master epoll ctl add leaf: " | errno | strerror(errno); 117 } 118 119 park(); 120 121 for() { 122 yield(); 123 int ret = epoll_wait(this.epollfd, events, MAX_EVENTS, 0); 124 if ( ret < 0 ) { 125 abort | "Leaf epoll error: " | errno | strerror(errno); 126 } 127 128 if(ret) { 129 for(i; ret) { 130 thread$ * thrd = (thread$ *)events[i].data.u64; 131 unpark( thrd, UNPARK_REMOTE ); 132 } 133 } 134 else if(0 >= --retries) { 135 epoll_rearm(master_epollfd, this.epollfd, EPOLLIN); 136 } 137 } 138 } 139 140 void setup_epoll( void ) __attribute__(( constructor )); 141 void setup_epoll( void ) { 142 if(master_epollfd) abort | "Master epoll already setup"; 143 144 master_epollfd = epoll_create1(0); 145 if(master_epollfd == -1) { 146 abort | "failed to create master epoll: " | errno | strerror(errno); 147 } 148 149 struct rlimit rlim; 150 if(int ret = getrlimit(RLIMIT_NOFILE, &rlim); 0 != ret) { 151 abort | "failed to get nofile limit: " | errno | strerror(errno); 152 } 153 154 fd_limit = rlim.rlim_cur; 155 fd_map = alloc(fd_limit); 156 for(i;fd_limit) { 157 fd_map[i] = 0p; 158 } 159 160 poller_cnt = 2; 161 poller_fds = alloc(poller_cnt); 162 pollers = alloc(poller_cnt); 163 for(i; poller_cnt) { 164 poller_fds[i] = epoll_create1(0); 165 if(poller_fds[i] == -1) { 166 abort | "failed to create leaf epoll [" | i | "]: " | errno | strerror(errno); 167 } 168 169 (pollers[i]){ poller_fds[i] }; 170 } 171 172 pthread_attr_t attr; 173 if (int ret = pthread_attr_init(&attr); 0 != ret) { 174 abort | "failed to create master epoll thread attr: " | ret | strerror(ret); 175 } 176 177 if (int ret = pthread_create(&master_poller, &attr, master_epoll, 0p); 0 != ret) { 178 abort | "failed to create master epoll thread: " | ret | strerror(ret); 179 } 180 } 181 182 static inline int epoll_wait(int fd, uint32_t event) { 183 if(fd_map[fd] >= 1p) { 184 fd_map[fd]->rearms++; 185 epoll_rearm(poller_fds[fd_map[fd]->pollid], fd, event); 186 return 0; 187 } 188 189 for() { 190 fd_info_t * expected = 0p; 191 fd_info_t * sentinel = 1p; 192 if(__atomic_compare_exchange_n( &(fd_map[fd]), &expected, sentinel, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED)) { 193 struct epoll_event eevent; 194 eevent.events = event | EPOLLET | EPOLLONESHOT; 195 eevent.data.u64 = (uint64_t)active_thread(); 196 197 int id = thread_rand() % poller_cnt; 198 if(0 != epoll_ctl(poller_fds[id], EPOLL_CTL_ADD, fd, &eevent)) 199 { 200 abort | "epoll ctl add" | poller_fds[id] | fd | fd_map[fd] | expected | "error: " | errno | strerror(errno); 201 } 202 203 fd_info_t * ninfo = alloc(); 204 ninfo->pollid = id; 205 ninfo->rearms = 0; 206 __atomic_store_n( &fd_map[fd], ninfo, __ATOMIC_SEQ_CST); 207 208 park(); 209 return 0; 210 } 211 212 if(expected >= 0) { 213 fd_map[fd]->rearms++; 214 epoll_rearm(poller_fds[fd_map[fd]->pollid], fd, event); 215 return 0; 216 } 217 218 Pause(); 219 } 220 } 221 #endif 222 223 //================================================================================ 224 // Thread run by the C Interface 34 225 35 226 struct cfathread_object { … … 245 436 // Mutex 246 437 struct cfathread_mutex { 247 fast_lock impl;438 linear_backoff_then_block_lock impl; 248 439 }; 249 440 int cfathread_mutex_init(cfathread_mutex_t *restrict mut, const cfathread_mutexattr_t *restrict) __attribute__((nonnull (1))) { *mut = new(); return 0; } … … 260 451 // Condition 261 452 struct cfathread_condition { 262 condition_variable( fast_lock) impl;453 condition_variable(linear_backoff_then_block_lock) impl; 263 454 }; 264 455 int cfathread_cond_init(cfathread_cond_t *restrict cond, const cfathread_condattr_t *restrict) __attribute__((nonnull (1))) { *cond = new(); return 0; } … … 288 479 // IO operations 289 480 int cfathread_socket(int domain, int type, int protocol) { 290 return socket(domain, type, protocol); 481 return socket(domain, type 482 #if defined(EPOLL_FOR_SOCKETS) 483 | SOCK_NONBLOCK 484 #endif 485 , protocol); 291 486 } 292 487 int cfathread_bind(int socket, const struct sockaddr *address, socklen_t address_len) { … … 299 494 300 495 int cfathread_accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len) { 301 return cfa_accept4(socket, address, address_len, 0, CFA_IO_LAZY); 496 #if defined(EPOLL_FOR_SOCKETS) 497 int ret; 498 for() { 499 yield(); 500 ret = accept4(socket, address, address_len, SOCK_NONBLOCK); 501 if(ret >= 0) break; 502 if(errno != EAGAIN && errno != EWOULDBLOCK) break; 503 504 epoll_wait(socket, EPOLLIN); 505 } 506 return ret; 507 #else 508 return cfa_accept4(socket, address, address_len, 0, CFA_IO_LAZY); 509 #endif 302 510 } 303 511 304 512 int cfathread_connect(int socket, const struct sockaddr *address, socklen_t address_len) { 305 return cfa_connect(socket, address, address_len, CFA_IO_LAZY); 513 #if defined(EPOLL_FOR_SOCKETS) 514 int ret; 515 for() { 516 ret = connect(socket, address, address_len); 517 if(ret >= 0) break; 518 if(errno != EAGAIN && errno != EWOULDBLOCK) break; 519 520 epoll_wait(socket, EPOLLIN); 521 } 522 return ret; 523 #else 524 return cfa_connect(socket, address, address_len, CFA_IO_LAZY); 525 #endif 306 526 } 307 527 … … 315 535 316 536 ssize_t cfathread_sendmsg(int socket, const struct msghdr *message, int flags) { 317 return cfa_sendmsg(socket, message, flags, CFA_IO_LAZY); 537 #if defined(EPOLL_FOR_SOCKETS) 538 ssize_t ret; 539 __STATS__( false, io.ops.sockwrite++; ) 540 for() { 541 ret = sendmsg(socket, message, flags); 542 if(ret >= 0) break; 543 if(errno != EAGAIN && errno != EWOULDBLOCK) break; 544 545 __STATS__( false, io.ops.epllwrite++; ) 546 epoll_wait(socket, EPOLLOUT); 547 } 548 #else 549 ssize_t ret = cfa_sendmsg(socket, message, flags, CFA_IO_LAZY); 550 #endif 551 return ret; 318 552 } 319 553 320 554 ssize_t cfathread_write(int fildes, const void *buf, size_t nbyte) { 321 555 // Use send rather then write for socket since it's faster 322 return cfa_send(fildes, buf, nbyte, 0, CFA_IO_LAZY); 556 #if defined(EPOLL_FOR_SOCKETS) 557 ssize_t ret; 558 // __STATS__( false, io.ops.sockwrite++; ) 559 for() { 560 ret = send(fildes, buf, nbyte, 0); 561 if(ret >= 0) break; 562 if(errno != EAGAIN && errno != EWOULDBLOCK) break; 563 564 // __STATS__( false, io.ops.epllwrite++; ) 565 epoll_wait(fildes, EPOLLOUT); 566 } 567 #else 568 ssize_t ret = cfa_send(fildes, buf, nbyte, 0, CFA_IO_LAZY); 569 #endif 570 return ret; 323 571 } 324 572 … … 336 584 msg.msg_controllen = 0; 337 585 338 ssize_t ret = cfa_recvmsg(socket, &msg, flags, CFA_IO_LAZY); 586 #if defined(EPOLL_FOR_SOCKETS) 587 ssize_t ret; 588 yield(); 589 for() { 590 ret = recvmsg(socket, &msg, flags); 591 if(ret >= 0) break; 592 if(errno != EAGAIN && errno != EWOULDBLOCK) break; 593 594 epoll_wait(socket, EPOLLIN); 595 } 596 #else 597 ssize_t ret = cfa_recvmsg(socket, &msg, flags, CFA_IO_LAZY); 598 #endif 339 599 340 600 if(address_len) *address_len = msg.msg_namelen; … … 344 604 ssize_t cfathread_read(int fildes, void *buf, size_t nbyte) { 345 605 // Use recv rather then read for socket since it's faster 346 return cfa_recv(fildes, buf, nbyte, 0, CFA_IO_LAZY); 347 } 348 349 } 606 #if defined(EPOLL_FOR_SOCKETS) 607 ssize_t ret; 608 __STATS__( false, io.ops.sockread++; ) 609 yield(); 610 for() { 611 ret = recv(fildes, buf, nbyte, 0); 612 if(ret >= 0) break; 613 if(errno != EAGAIN && errno != EWOULDBLOCK) break; 614 615 __STATS__( false, io.ops.epllread++; ) 616 epoll_wait(fildes, EPOLLIN); 617 } 618 #else 619 ssize_t ret = cfa_recv(fildes, buf, nbyte, 0, CFA_IO_LAZY); 620 #endif 621 return ret; 622 } 623 624 } -
libcfa/src/concurrency/invoke.h
rf95634e rb7fd9daf 170 170 bool corctx_flag; 171 171 172 int last_cpu;173 174 172 //SKULLDUGGERY errno is not save in the thread data structure because returnToKernel appears to be the only function to require saving and restoring it 175 173 … … 177 175 struct cluster * curr_cluster; 178 176 179 // preferred ready-queue 177 // preferred ready-queue or CPU 180 178 unsigned preferred; 181 179 -
libcfa/src/concurrency/io.cfa
rf95634e rb7fd9daf 90 90 static inline unsigned __flush( struct $io_context & ); 91 91 static inline __u32 __release_sqes( struct $io_context & ); 92 extern void __kernel_unpark( thread$ * thrd );92 extern void __kernel_unpark( thread$ * thrd, unpark_hint ); 93 93 94 94 bool __cfa_io_drain( processor * proc ) { … … 118 118 __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future ); 119 119 120 __kernel_unpark( fulfil( *future, cqe.res, false ) );120 __kernel_unpark( fulfil( *future, cqe.res, false ), UNPARK_LOCAL ); 121 121 } 122 122 … … 183 183 ctx.proc->io.pending = false; 184 184 185 ready_schedule_lock();186 185 __cfa_io_drain( proc ); 187 ready_schedule_unlock();188 186 // for(i; 2) { 189 187 // unsigned idx = proc->rdq.id + i; … … 311 309 // Make the sqes visible to the submitter 312 310 __atomic_store_n(sq.kring.tail, tail + have, __ATOMIC_RELEASE); 313 sq.to_submit ++;311 sq.to_submit += have; 314 312 315 313 ctx->proc->io.pending = true; 316 314 ctx->proc->io.dirty = true; 317 315 if(sq.to_submit > 30 || !lazy) { 316 ready_schedule_lock(); 318 317 __cfa_io_flush( ctx->proc ); 318 ready_schedule_unlock(); 319 319 } 320 320 } -
libcfa/src/concurrency/io/types.hfa
rf95634e rb7fd9daf 188 188 return wait(this.self); 189 189 } 190 191 void reset( io_future_t & this ) { 192 return reset(this.self); 193 } 190 194 } -
libcfa/src/concurrency/kernel.cfa
rf95634e rb7fd9daf 22 22 #include <errno.h> 23 23 #include <stdio.h> 24 #include <string.h> 24 25 #include <signal.h> 25 26 #include <unistd.h> … … 31 32 #include "kernel_private.hfa" 32 33 #include "preemption.hfa" 34 #include "strstream.hfa" 35 #include "device/cpu.hfa" 33 36 34 37 //Private includes … … 193 196 194 197 if( !readyThread ) { 198 ready_schedule_lock(); 195 199 __cfa_io_flush( this ); 200 ready_schedule_unlock(); 201 196 202 readyThread = __next_thread_slow( this->cltr ); 197 203 } … … 231 237 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle); 232 238 233 __disable_interrupts_hard(); 234 eventfd_t val; 235 eventfd_read( this->idle, &val ); 236 __enable_interrupts_hard(); 239 { 240 eventfd_t val; 241 ssize_t ret = read( this->idle, &val, sizeof(val) ); 242 if(ret < 0) { 243 switch((int)errno) { 244 case EAGAIN: 245 #if EAGAIN != EWOULDBLOCK 246 case EWOULDBLOCK: 247 #endif 248 case EINTR: 249 // No need to do anything special here, just assume it's a legitimate wake-up 250 break; 251 default: 252 abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) ); 253 } 254 } 255 } 237 256 238 257 #if !defined(__CFA_NO_STATISTICS__) … … 261 280 262 281 if(this->io.pending && !this->io.dirty) { 282 ready_schedule_lock(); 263 283 __cfa_io_flush( this ); 284 ready_schedule_unlock(); 264 285 } 265 286 … … 301 322 302 323 // Don't block if we are done 303 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 324 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) { 325 ready_schedule_unlock(); 326 break MAIN_LOOP; 327 } 304 328 305 329 __STATS( __tls_stats()->ready.sleep.halts++; ) … … 325 349 } 326 350 327 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )351 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); ) 328 352 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle); 329 353 330 // __disable_interrupts_hard(); 331 eventfd_t val; 332 eventfd_read( this->idle, &val ); 333 // __enable_interrupts_hard(); 354 { 355 eventfd_t val; 356 ssize_t ret = read( this->idle, &val, sizeof(val) ); 357 if(ret < 0) { 358 switch((int)errno) { 359 case EAGAIN: 360 #if EAGAIN != EWOULDBLOCK 361 case EWOULDBLOCK: 362 #endif 363 case EINTR: 364 // No need to do anything special here, just assume it's a legitimate wake-up 365 break; 366 default: 367 abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) ); 368 } 369 } 370 } 334 371 335 372 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); ) … … 393 430 /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next ); 394 431 __builtin_prefetch( thrd_dst->context.SP ); 395 396 int curr = __kernel_getcpu();397 if(thrd_dst->last_cpu != curr) {398 int64_t l = thrd_dst->last_cpu;399 int64_t c = curr;400 int64_t v = (l << 32) | c;401 __push_stat( __tls_stats(), v, false, "Processor", this );402 }403 404 thrd_dst->last_cpu = curr;405 432 406 433 __cfadbg_print_safe(runtime_core, "Kernel : core %p running thread %p (%s)\n", this, thrd_dst, thrd_dst->self_cor.name); … … 457 484 if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) { 458 485 // The thread was preempted, reschedule it and reset the flag 459 schedule_thread$( thrd_dst );486 schedule_thread$( thrd_dst, UNPARK_LOCAL ); 460 487 break RUNNING; 461 488 } … … 541 568 // Scheduler routines 542 569 // KERNEL ONLY 543 static void __schedule_thread( thread$ * thrd ) {570 static void __schedule_thread( thread$ * thrd, unpark_hint hint ) { 544 571 /* paranoid */ verify( ! __preemption_enabled() ); 545 572 /* paranoid */ verify( ready_schedule_islocked()); … … 561 588 // Dereference the thread now because once we push it, there is not guaranteed it's still valid. 562 589 struct cluster * cl = thrd->curr_cluster; 563 __STATS(bool outside = thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )590 __STATS(bool outside = hint == UNPARK_LOCAL && thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; ) 564 591 565 592 // push the thread to the cluster ready-queue 566 push( cl, thrd, local);593 push( cl, thrd, hint ); 567 594 568 595 // variable thrd is no longer safe to use … … 589 616 } 590 617 591 void schedule_thread$( thread$ * thrd ) {618 void schedule_thread$( thread$ * thrd, unpark_hint hint ) { 592 619 ready_schedule_lock(); 593 __schedule_thread( thrd );620 __schedule_thread( thrd, hint ); 594 621 ready_schedule_unlock(); 595 622 } … … 642 669 } 643 670 644 void __kernel_unpark( thread$ * thrd ) {671 void __kernel_unpark( thread$ * thrd, unpark_hint hint ) { 645 672 /* paranoid */ verify( ! __preemption_enabled() ); 646 673 /* paranoid */ verify( ready_schedule_islocked()); … … 650 677 if(__must_unpark(thrd)) { 651 678 // Wake lost the race, 652 __schedule_thread( thrd );679 __schedule_thread( thrd, hint ); 653 680 } 654 681 … … 657 684 } 658 685 659 void unpark( thread$ * thrd ) {686 void unpark( thread$ * thrd, unpark_hint hint ) { 660 687 if( !thrd ) return; 661 688 … … 663 690 disable_interrupts(); 664 691 // Wake lost the race, 665 schedule_thread$( thrd );692 schedule_thread$( thrd, hint ); 666 693 enable_interrupts(false); 667 694 } … … 920 947 /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count); 921 948 /* paranoid */ verify( it->local_data->this_stats ); 949 // __print_stats( it->local_data->this_stats, cltr->print_stats, "Processor", it->name, (void*)it ); 922 950 __tally_stats( cltr->stats, it->local_data->this_stats ); 923 951 it = &(*it)`next; … … 929 957 // this doesn't solve all problems but does solve many 930 958 // so it's probably good enough 959 disable_interrupts(); 931 960 uint_fast32_t last_size = ready_mutate_lock(); 932 961 … … 936 965 // Unlock the RWlock 937 966 ready_mutate_unlock( last_size ); 967 enable_interrupts(); 938 968 } 939 969 -
libcfa/src/concurrency/kernel.hfa
rf95634e rb7fd9daf 151 151 struct __attribute__((aligned(128))) __timestamp_t { 152 152 volatile unsigned long long tv; 153 }; 154 155 static inline void ?{}(__timestamp_t & this) { this.tv = 0; } 153 volatile unsigned long long ma; 154 }; 155 156 // Aligned timestamps which are used by the relaxed ready queue 157 struct __attribute__((aligned(128))) __help_cnts_t { 158 volatile unsigned long long src; 159 volatile unsigned long long dst; 160 volatile unsigned long long tri; 161 }; 162 163 static inline void ?{}(__timestamp_t & this) { this.tv = 0; this.ma = 0; } 156 164 static inline void ^?{}(__timestamp_t & this) {} 157 165 … … 169 177 // Array of times 170 178 __timestamp_t * volatile tscs; 179 180 // Array of stats 181 __help_cnts_t * volatile help; 171 182 172 183 // Number of lanes (empty or not) -
libcfa/src/concurrency/kernel/fwd.hfa
rf95634e rb7fd9daf 119 119 120 120 extern "Cforall" { 121 enum unpark_hint { UNPARK_LOCAL, UNPARK_REMOTE }; 122 121 123 extern void park( void ); 122 extern void unpark( struct thread$ * this ); 124 extern void unpark( struct thread$ *, unpark_hint ); 125 static inline void unpark( struct thread$ * thrd ) { unpark(thrd, UNPARK_LOCAL); } 123 126 static inline struct thread$ * active_thread () { 124 127 struct thread$ * t = publicTLS_get( this_thread ); -
libcfa/src/concurrency/kernel/startup.cfa
rf95634e rb7fd9daf 100 100 // Other Forward Declarations 101 101 extern void __wake_proc(processor *); 102 extern int cfa_main_returned; // from interpose.cfa 102 103 103 104 //----------------------------------------------------------------------------- … … 200 201 __cfadbg_print_safe(runtime_core, "Kernel : Main cluster ready\n"); 201 202 203 // Construct the processor context of the main processor 204 void ?{}(processorCtx_t & this, processor * proc) { 205 (this.__cor){ "Processor" }; 206 this.__cor.starter = 0p; 207 this.proc = proc; 208 } 209 210 void ?{}(processor & this) with( this ) { 211 ( this.terminated ){}; 212 ( this.runner ){}; 213 init( this, "Main Processor", *mainCluster, 0p ); 214 kernel_thread = pthread_self(); 215 216 runner{ &this }; 217 __cfadbg_print_safe(runtime_core, "Kernel : constructed main processor context %p\n", &runner); 218 } 219 220 // Initialize the main processor and the main processor ctx 221 // (the coroutine that contains the processing control flow) 222 mainProcessor = (processor *)&storage_mainProcessor; 223 (*mainProcessor){}; 224 225 register_tls( mainProcessor ); 226 202 227 // Start by initializing the main thread 203 228 // SKULLDUGGERY: the mainThread steals the process main thread … … 210 235 __cfadbg_print_safe(runtime_core, "Kernel : Main thread ready\n"); 211 236 212 213 214 // Construct the processor context of the main processor215 void ?{}(processorCtx_t & this, processor * proc) {216 (this.__cor){ "Processor" };217 this.__cor.starter = 0p;218 this.proc = proc;219 }220 221 void ?{}(processor & this) with( this ) {222 ( this.terminated ){};223 ( this.runner ){};224 init( this, "Main Processor", *mainCluster, 0p );225 kernel_thread = pthread_self();226 227 runner{ &this };228 __cfadbg_print_safe(runtime_core, "Kernel : constructed main processor context %p\n", &runner);229 }230 231 // Initialize the main processor and the main processor ctx232 // (the coroutine that contains the processing control flow)233 mainProcessor = (processor *)&storage_mainProcessor;234 (*mainProcessor){};235 236 register_tls( mainProcessor );237 mainThread->last_cpu = __kernel_getcpu();238 239 237 //initialize the global state variables 240 238 __cfaabi_tls.this_processor = mainProcessor; … … 252 250 // Add the main thread to the ready queue 253 251 // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread 254 schedule_thread$(mainThread );252 schedule_thread$(mainThread, UNPARK_LOCAL); 255 253 256 254 // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX … … 271 269 272 270 static void __kernel_shutdown(void) { 271 if(!cfa_main_returned) return; 273 272 /* paranoid */ verify( __preemption_enabled() ); 274 273 disable_interrupts(); … … 486 485 link.next = 0p; 487 486 link.ts = -1llu; 488 preferred = -1u;487 preferred = ready_queue_new_preferred(); 489 488 last_proc = 0p; 490 489 #if defined( __CFA_WITH_VERIFY__ ) -
libcfa/src/concurrency/kernel_private.hfa
rf95634e rb7fd9daf 46 46 } 47 47 48 void schedule_thread$( thread$ * ) __attribute__((nonnull (1)));48 void schedule_thread$( thread$ *, unpark_hint hint ) __attribute__((nonnull (1))); 49 49 50 50 extern bool __preemption_enabled(); … … 300 300 // push thread onto a ready queue for a cluster 301 301 // returns true if the list was previously empty, false otherwise 302 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool local);302 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint); 303 303 304 304 //----------------------------------------------------------------------- … … 321 321 322 322 //----------------------------------------------------------------------- 323 // get preferred ready for new thread 324 unsigned ready_queue_new_preferred(); 325 326 //----------------------------------------------------------------------- 323 327 // Increase the width of the ready queue (number of lanes) by 4 324 328 void ready_queue_grow (struct cluster * cltr); -
libcfa/src/concurrency/locks.hfa
rf95634e rb7fd9daf 324 324 } 325 325 326 // linear backoff bounded by spin_count327 spin = spin_start;328 int spin_counter = 0;329 int yield_counter = 0;330 for ( ;; ) {331 if(try_lock_contention(this)) return true;332 if(spin_counter < spin_count) {333 for (int i = 0; i < spin; i++) Pause();334 if (spin < spin_end) spin += spin;335 else spin_counter++;336 } else if (yield_counter < yield_count) {337 // after linear backoff yield yield_count times338 yield_counter++;339 yield();340 } else { break; }341 }342 343 // block until signalled344 while (block(this)) if(try_lock_contention(this)) return true;345 346 // this should never be reached as block(this) always returns true347 return false;348 }349 350 static inline bool lock_improved(linear_backoff_then_block_lock & this) with(this) {351 // if owner just return352 if (active_thread() == owner) return true;353 size_t compare_val = 0;354 int spin = spin_start;355 // linear backoff356 for( ;; ) {357 compare_val = 0;358 if (internal_try_lock(this, compare_val)) return true;359 if (2 == compare_val) break;360 for (int i = 0; i < spin; i++) Pause();361 if (spin >= spin_end) break;362 spin += spin;363 }364 365 // linear backoff bounded by spin_count366 spin = spin_start;367 int spin_counter = 0;368 int yield_counter = 0;369 for ( ;; ) {370 compare_val = 0;371 if(internal_try_lock(this, compare_val)) return true;372 if (2 == compare_val) break;373 if(spin_counter < spin_count) {374 for (int i = 0; i < spin; i++) Pause();375 if (spin < spin_end) spin += spin;376 else spin_counter++;377 } else if (yield_counter < yield_count) {378 // after linear backoff yield yield_count times379 yield_counter++;380 yield();381 } else { break; }382 }383 384 326 if(2 != compare_val && try_lock_contention(this)) return true; 385 327 // block until signalled … … 402 344 static inline void on_notify(linear_backoff_then_block_lock & this, struct thread$ * t ) { unpark(t); } 403 345 static inline size_t on_wait(linear_backoff_then_block_lock & this) { unlock(this); return 0; } 404 static inline void on_wakeup(linear_backoff_then_block_lock & this, size_t recursion ) { lock _improved(this); }346 static inline void on_wakeup(linear_backoff_then_block_lock & this, size_t recursion ) { lock(this); } 405 347 406 348 //----------------------------------------------------------------------------- -
libcfa/src/concurrency/monitor.cfa
rf95634e rb7fd9daf 990 990 } 991 991 992 //----------------------------------------------------------------------------- 993 // Enter routine for mutex stmt 994 // Can't be accepted since a mutex stmt is effectively an anonymous routine 995 // Thus we do not need a monitor group 996 void lock( monitor$ * this ) { 997 thread$ * thrd = active_thread(); 998 999 // Lock the monitor spinlock 1000 lock( this->lock __cfaabi_dbg_ctx2 ); 1001 1002 __cfaabi_dbg_print_safe( "Kernel : %10p Entering mon %p (%p)\n", thrd, this, this->owner); 1003 1004 if( unlikely(0 != (0x1 & (uintptr_t)this->owner)) ) { 1005 abort( "Attempt by thread \"%.256s\" (%p) to access joined monitor %p.", thrd->self_cor.name, thrd, this ); 1006 } 1007 else if( !this->owner ) { 1008 // No one has the monitor, just take it 1009 __set_owner( this, thrd ); 1010 1011 __cfaabi_dbg_print_safe( "Kernel : mon is free \n" ); 1012 } 1013 else if( this->owner == thrd) { 1014 // We already have the monitor, just note how many times we took it 1015 this->recursion += 1; 1016 1017 __cfaabi_dbg_print_safe( "Kernel : mon already owned \n" ); 1018 } 1019 else { 1020 __cfaabi_dbg_print_safe( "Kernel : blocking \n" ); 1021 1022 // Some one else has the monitor, wait in line for it 1023 /* paranoid */ verify( thrd->link.next == 0p ); 1024 append( this->entry_queue, thrd ); 1025 /* paranoid */ verify( thrd->link.next == 1p ); 1026 1027 unlock( this->lock ); 1028 park(); 1029 1030 __cfaabi_dbg_print_safe( "Kernel : %10p Entered mon %p\n", thrd, this); 1031 1032 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 1033 return; 1034 } 1035 1036 __cfaabi_dbg_print_safe( "Kernel : %10p Entered mon %p\n", thrd, this); 1037 1038 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 1039 /* paranoid */ verify( this->lock.lock ); 1040 1041 // Release the lock and leave 1042 unlock( this->lock ); 1043 return; 1044 } 1045 1046 // Leave routine for mutex stmt 1047 // Is just a wrapper around __leave for the is_lock trait to see 1048 void unlock( monitor$ * this ) { __leave( this ); } 1049 992 1050 // Local Variables: // 993 1051 // mode: c // -
libcfa/src/concurrency/monitor.hfa
rf95634e rb7fd9daf 65 65 free( th ); 66 66 } 67 68 static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } ) 69 void adelete( T arr[] ) { 70 if ( arr ) { // ignore null 71 size_t dim = malloc_size( arr ) / sizeof( T ); 72 for ( int i = dim - 1; i >= 0; i -= 1 ) { // reverse allocation order, must be unsigned 73 ^(arr[i]){}; // run destructor 74 } // for 75 free( arr ); 76 } // if 77 } // adelete 67 78 68 79 //----------------------------------------------------------------------------- … … 149 160 void __waitfor_internal( const __waitfor_mask_t & mask, int duration ); 150 161 162 // lock and unlock routines for mutex statements to use 163 void lock( monitor$ * this ); 164 void unlock( monitor$ * this ); 165 151 166 // Local Variables: // 152 167 // mode: c // -
libcfa/src/concurrency/ready_queue.cfa
rf95634e rb7fd9daf 100 100 #define __kernel_rseq_unregister rseq_unregister_current_thread 101 101 #elif defined(CFA_HAVE_LINUX_RSEQ_H) 102 void __kernel_raw_rseq_register (void);103 void __kernel_raw_rseq_unregister(void);102 static void __kernel_raw_rseq_register (void); 103 static void __kernel_raw_rseq_unregister(void); 104 104 105 105 #define __kernel_rseq_register __kernel_raw_rseq_register … … 246 246 // Cforall Ready Queue used for scheduling 247 247 //======================================================================= 248 unsigned long long moving_average(unsigned long long nval, unsigned long long oval) { 249 const unsigned long long tw = 16; 250 const unsigned long long nw = 4; 251 const unsigned long long ow = tw - nw; 252 return ((nw * nval) + (ow * oval)) / tw; 253 } 254 248 255 void ?{}(__ready_queue_t & this) with (this) { 249 256 #if defined(USE_CPU_WORK_STEALING) … … 251 258 lanes.data = alloc( lanes.count ); 252 259 lanes.tscs = alloc( lanes.count ); 260 lanes.help = alloc( cpu_info.hthrd_count ); 253 261 254 262 for( idx; (size_t)lanes.count ) { 255 263 (lanes.data[idx]){}; 256 264 lanes.tscs[idx].tv = rdtscl(); 265 lanes.tscs[idx].ma = rdtscl(); 266 } 267 for( idx; (size_t)cpu_info.hthrd_count ) { 268 lanes.help[idx].src = 0; 269 lanes.help[idx].dst = 0; 270 lanes.help[idx].tri = 0; 257 271 } 258 272 #else 259 273 lanes.data = 0p; 260 274 lanes.tscs = 0p; 275 lanes.help = 0p; 261 276 lanes.count = 0; 262 277 #endif … … 270 285 free(lanes.data); 271 286 free(lanes.tscs); 287 free(lanes.help); 272 288 } 273 289 274 290 //----------------------------------------------------------------------- 275 291 #if defined(USE_CPU_WORK_STEALING) 276 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {292 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) { 277 293 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 278 294 279 295 processor * const proc = kernelTLS().this_processor; 280 const bool external = !push_local || (!proc) || (cltr != proc->cltr); 281 296 const bool external = (!proc) || (cltr != proc->cltr); 297 298 // Figure out the current cpu and make sure it is valid 282 299 const int cpu = __kernel_getcpu(); 283 300 /* paranoid */ verify(cpu >= 0); … … 285 302 /* paranoid */ verify(cpu * READYQ_SHARD_FACTOR < lanes.count); 286 303 287 const cpu_map_entry_t & map = cpu_info.llc_map[cpu]; 304 // Figure out where thread was last time and make sure it's 305 /* paranoid */ verify(thrd->preferred >= 0); 306 /* paranoid */ verify(thrd->preferred < cpu_info.hthrd_count); 307 /* paranoid */ verify(thrd->preferred * READYQ_SHARD_FACTOR < lanes.count); 308 const int prf = thrd->preferred * READYQ_SHARD_FACTOR; 309 310 const cpu_map_entry_t & map; 311 choose(hint) { 312 case UNPARK_LOCAL : &map = &cpu_info.llc_map[cpu]; 313 case UNPARK_REMOTE: &map = &cpu_info.llc_map[prf]; 314 } 288 315 /* paranoid */ verify(map.start * READYQ_SHARD_FACTOR < lanes.count); 289 316 /* paranoid */ verify(map.self * READYQ_SHARD_FACTOR < lanes.count); … … 296 323 if(unlikely(external)) { r = __tls_rand(); } 297 324 else { r = proc->rdq.its++; } 298 i = start + (r % READYQ_SHARD_FACTOR); 325 choose(hint) { 326 case UNPARK_LOCAL : i = start + (r % READYQ_SHARD_FACTOR); 327 case UNPARK_REMOTE: i = prf + (r % READYQ_SHARD_FACTOR); 328 } 299 329 // If we can't lock it retry 300 330 } while( !__atomic_try_acquire( &lanes.data[i].lock ) ); … … 332 362 processor * const proc = kernelTLS().this_processor; 333 363 const int start = map.self * READYQ_SHARD_FACTOR; 364 const unsigned long long ctsc = rdtscl(); 334 365 335 366 // Did we already have a help target 336 367 if(proc->rdq.target == -1u) { 337 // if We don't have a 338 unsigned long long min = ts(lanes.data[start]); 368 unsigned long long max = 0; 339 369 for(i; READYQ_SHARD_FACTOR) { 340 unsigned long long tsc = ts(lanes.data[start + i]); 341 if(tsc < min) min = tsc; 342 } 343 proc->rdq.cutoff = min; 344 370 unsigned long long tsc = moving_average(ctsc - ts(lanes.data[start + i]), lanes.tscs[start + i].ma); 371 if(tsc > max) max = tsc; 372 } 373 proc->rdq.cutoff = (max + 2 * max) / 2; 345 374 /* paranoid */ verify(lanes.count < 65536); // The following code assumes max 65536 cores. 346 375 /* paranoid */ verify(map.count < 65536); // The following code assumes max 65536 cores. 347 376 348 if(0 == (__tls_rand() % 10 _000)) {377 if(0 == (__tls_rand() % 100)) { 349 378 proc->rdq.target = __tls_rand() % lanes.count; 350 379 } else { … … 358 387 } 359 388 else { 360 const unsigned long long bias = 0; //2_500_000_000; 361 const unsigned long long cutoff = proc->rdq.cutoff > bias ? proc->rdq.cutoff - bias : proc->rdq.cutoff; 389 unsigned long long max = 0; 390 for(i; READYQ_SHARD_FACTOR) { 391 unsigned long long tsc = moving_average(ctsc - ts(lanes.data[start + i]), lanes.tscs[start + i].ma); 392 if(tsc > max) max = tsc; 393 } 394 const unsigned long long cutoff = (max + 2 * max) / 2; 362 395 { 363 396 unsigned target = proc->rdq.target; 364 397 proc->rdq.target = -1u; 365 if(lanes.tscs[target].tv < cutoff && ts(lanes.data[target]) < cutoff) { 398 lanes.help[target / READYQ_SHARD_FACTOR].tri++; 399 if(moving_average(ctsc - lanes.tscs[target].tv, lanes.tscs[target].ma) > cutoff) { 366 400 thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help)); 367 401 proc->rdq.last = target; 368 402 if(t) return t; 403 else proc->rdq.target = -1u; 369 404 } 405 else proc->rdq.target = -1u; 370 406 } 371 407 … … 428 464 } 429 465 430 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {466 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) { 431 467 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 432 468 433 const bool external = !push_local|| (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);469 const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 434 470 /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count ); 435 471 … … 515 551 #endif 516 552 #if defined(USE_WORK_STEALING) 517 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {553 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) { 518 554 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 519 555 520 556 // #define USE_PREFERRED 521 557 #if !defined(USE_PREFERRED) 522 const bool external = !push_local|| (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);558 const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 523 559 /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count ); 524 560 #else 525 561 unsigned preferred = thrd->preferred; 526 const bool external = push_local|| (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr;562 const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr; 527 563 /* paranoid */ verifyf(external || preferred < lanes.count, "Invalid preferred queue %u for %u lanes", preferred, lanes.count ); 528 564 … … 645 681 // Actually pop the list 646 682 struct thread$ * thrd; 683 unsigned long long tsc_before = ts(lane); 647 684 unsigned long long tsv; 648 685 [thrd, tsv] = pop(lane); … … 658 695 __STATS( stats.success++; ) 659 696 660 #if defined(USE_WORK_STEALING) 697 #if defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING) 698 unsigned long long now = rdtscl(); 661 699 lanes.tscs[w].tv = tsv; 700 lanes.tscs[w].ma = moving_average(now > tsc_before ? now - tsc_before : 0, lanes.tscs[w].ma); 662 701 #endif 663 702 664 thrd->preferred = w; 703 #if defined(USE_CPU_WORK_STEALING) 704 thrd->preferred = w / READYQ_SHARD_FACTOR; 705 #else 706 thrd->preferred = w; 707 #endif 665 708 666 709 // return the popped thread … … 688 731 689 732 //----------------------------------------------------------------------- 733 // get preferred ready for new thread 734 unsigned ready_queue_new_preferred() { 735 unsigned pref = 0; 736 if(struct thread$ * thrd = publicTLS_get( this_thread )) { 737 pref = thrd->preferred; 738 } 739 else { 740 #if defined(USE_CPU_WORK_STEALING) 741 pref = __kernel_getcpu(); 742 #endif 743 } 744 745 #if defined(USE_CPU_WORK_STEALING) 746 /* paranoid */ verify(pref >= 0); 747 /* paranoid */ verify(pref < cpu_info.hthrd_count); 748 #endif 749 750 return pref; 751 } 752 753 //----------------------------------------------------------------------- 690 754 // Check that all the intrusive queues in the data structure are still consistent 691 755 static void check( __ready_queue_t & q ) with (q) { … … 915 979 extern void __enable_interrupts_hard(); 916 980 917 void __kernel_raw_rseq_register (void) {981 static void __kernel_raw_rseq_register (void) { 918 982 /* paranoid */ verify( __cfaabi_rseq.cpu_id == RSEQ_CPU_ID_UNINITIALIZED ); 919 983 … … 933 997 } 934 998 935 void __kernel_raw_rseq_unregister(void) {999 static void __kernel_raw_rseq_unregister(void) { 936 1000 /* paranoid */ verify( __cfaabi_rseq.cpu_id >= 0 ); 937 1001 -
libcfa/src/concurrency/ready_subqueue.hfa
rf95634e rb7fd9daf 98 98 99 99 // Get the relevant nodes locally 100 unsigned long long ts = this.anchor.ts;101 100 thread$ * node = this.anchor.next; 102 101 this.anchor.next = node->link.next; … … 116 115 /* paranoid */ verify( node->link.ts != 0 ); 117 116 /* paranoid */ verify( this.anchor.ts != 0 ); 118 return [node, t s];117 return [node, this.anchor.ts]; 119 118 } 120 119 -
libcfa/src/concurrency/stats.cfa
rf95634e rb7fd9daf 48 48 stats->io.calls.completed = 0; 49 49 stats->io.calls.errors.busy = 0; 50 stats->io.ops.sockread = 0; 51 stats->io.ops.epllread = 0; 52 stats->io.ops.sockwrite = 0; 53 stats->io.ops.epllwrite = 0; 50 54 #endif 51 55 … … 104 108 tally_one( &cltr->io.calls.completed , &proc->io.calls.completed ); 105 109 tally_one( &cltr->io.calls.errors.busy, &proc->io.calls.errors.busy ); 110 tally_one( &cltr->io.ops.sockread , &proc->io.ops.sockread ); 111 tally_one( &cltr->io.ops.epllread , &proc->io.ops.epllread ); 112 tally_one( &cltr->io.ops.sockwrite , &proc->io.ops.sockwrite ); 113 tally_one( &cltr->io.ops.epllwrite , &proc->io.ops.epllwrite ); 106 114 #endif 107 115 } … … 179 187 | " - cmp " | eng3(io.calls.drain) | "/" | eng3(io.calls.completed) | "(" | ws(3, 3, avgcomp) | "/drain)" 180 188 | " - " | eng3(io.calls.errors.busy) | " EBUSY"; 189 sstr | "- ops blk: " 190 | " sk rd: " | eng3(io.ops.sockread) | "epll: " | eng3(io.ops.epllread) 191 | " sk wr: " | eng3(io.ops.sockwrite) | "epll: " | eng3(io.ops.epllwrite); 181 192 sstr | nl; 182 193 } -
libcfa/src/concurrency/stats.hfa
rf95634e rb7fd9daf 102 102 volatile uint64_t sleeps; 103 103 } poller; 104 struct { 105 volatile uint64_t sockread; 106 volatile uint64_t epllread; 107 volatile uint64_t sockwrite; 108 volatile uint64_t epllwrite; 109 } ops; 104 110 }; 105 111 #endif -
libcfa/src/concurrency/thread.cfa
rf95634e rb7fd9daf 25 25 #include "invoke.h" 26 26 27 uint64_t thread_rand(); 28 27 29 //----------------------------------------------------------------------------- 28 30 // Thread ctors and dtors … … 34 36 preempted = __NO_PREEMPTION; 35 37 corctx_flag = false; 36 disable_interrupts();37 last_cpu = __kernel_getcpu();38 enable_interrupts();39 38 curr_cor = &self_cor; 40 39 self_mon.owner = &this; … … 44 43 link.next = 0p; 45 44 link.ts = -1llu; 46 preferred = -1u;45 preferred = ready_queue_new_preferred(); 47 46 last_proc = 0p; 48 47 #if defined( __CFA_WITH_VERIFY__ ) … … 141 140 /* paranoid */ verify( this_thrd->context.SP ); 142 141 143 schedule_thread$( this_thrd );142 schedule_thread$( this_thrd, UNPARK_LOCAL ); 144 143 enable_interrupts(); 145 144 } -
libcfa/src/device/cpu.cfa
rf95634e rb7fd9daf 144 144 // Count number of cpus in the system 145 145 static int count_cpus(void) { 146 const char * fpath = "/sys/devices/system/cpu/ present";146 const char * fpath = "/sys/devices/system/cpu/online"; 147 147 int fd = open(fpath, 0, O_RDONLY); 148 148 /* paranoid */ verifyf(fd >= 0, "Could not open file %s", fpath); … … 422 422 } 423 423 } 424 425 cpu_info_t cpu_info; -
libcfa/src/device/cpu.hfa
rf95634e rb7fd9daf 30 30 }; 31 31 32 cpu_info_t cpu_info;32 extern cpu_info_t cpu_info; -
libcfa/src/fstream.cfa
rf95634e rb7fd9daf 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Jul 29 22:34:10202113 // Update Count : 45412 // Last Modified On : Sun Oct 10 11:23:05 2021 13 // Update Count : 512 14 14 // 15 15 … … 28 28 #define IO_MSG "I/O error: " 29 29 30 void ?{}( ofstream & os, void * file ) { 31 os.file$ = file; 32 os.sepDefault$ = true;33 os.sepOnOff$ = false;34 os.nlOnOff$ = true;35 os.prt$ = false;36 os.sawNL$ = false;37 os.acquired$ = false;30 // private 31 void ?{}( ofstream & os, void * file ) with( os ) { 32 file$ = file; 33 sepDefault$ = true; 34 sepOnOff$ = false; 35 nlOnOff$ = true; 36 prt$ = false; 37 sawNL$ = false; 38 38 sepSetCur$( os, sepGet( os ) ); 39 39 sepSet( os, " " ); … … 41 41 } // ?{} 42 42 43 // private 44 bool sepPrt$( ofstream & os ) { setNL$( os, false ); return os.sepOnOff$; } 45 void sepReset$( ofstream & os ) { os.sepOnOff$ = os.sepDefault$; } 46 void sepReset$( ofstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; } 47 const char * sepGetCur$( ofstream & os ) { return os.sepCur$; } 48 void sepSetCur$( ofstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; } 49 bool getNL$( ofstream & os ) { return os.sawNL$; } 50 void setNL$( ofstream & os, bool state ) { os.sawNL$ = state; } 51 bool getANL$( ofstream & os ) { return os.nlOnOff$; } 52 bool getPrt$( ofstream & os ) { return os.prt$; } 53 void setPrt$( ofstream & os, bool state ) { os.prt$ = state; } 43 inline bool sepPrt$( ofstream & os ) { setNL$( os, false ); return os.sepOnOff$; } 44 inline void sepReset$( ofstream & os ) { os.sepOnOff$ = os.sepDefault$; } 45 inline void sepReset$( ofstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; } 46 inline const char * sepGetCur$( ofstream & os ) { return os.sepCur$; } 47 inline void sepSetCur$( ofstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; } 48 inline bool getNL$( ofstream & os ) { return os.sawNL$; } 49 inline void setNL$( ofstream & os, bool state ) { os.sawNL$ = state; } 50 inline bool getANL$( ofstream & os ) { return os.nlOnOff$; } 51 inline bool getPrt$( ofstream & os ) { return os.prt$; } 52 inline void setPrt$( ofstream & os, bool state ) { os.prt$ = state; } 53 54 inline void lock( ofstream & os ) with( os ) { lock( os.lock$ ); } 55 inline void unlock( ofstream & os ) { unlock( os.lock$ ); } 54 56 55 57 // public 56 58 void ?{}( ofstream & os ) { os.file$ = 0p; } 57 58 void ?{}( ofstream & os, const char name[], const char mode[] ) { 59 open( os, name, mode ); 60 } // ?{} 61 62 void ?{}( ofstream & os, const char name[] ) { 63 open( os, name, "w" ); 64 } // ?{} 65 66 void ^?{}( ofstream & os ) { 67 close( os ); 68 } // ^?{} 59 void ?{}( ofstream & os, const char name[], const char mode[] ) { open( os, name, mode ); } 60 void ?{}( ofstream & os, const char name[] ) { open( os, name, "w" ); } 61 void ^?{}( ofstream & os ) { close( os ); } 69 62 70 63 void sepOn( ofstream & os ) { os.sepOnOff$ = ! getNL$( os ); } … … 107 100 if ( &os == &exit ) exit( EXIT_FAILURE ); 108 101 if ( &os == &abort ) abort(); 109 if ( os.acquired$ ) { os.acquired$ = false; release( os ); }110 102 } // ends 111 103 112 bool fail( ofstream & os ) { 113 return os.file$ == 0 || ferror( (FILE *)(os.file$) ); 114 } // fail 115 116 void clear( ofstream & os ) { 117 clearerr( (FILE *)(os.file$) ); 118 } // clear 119 120 int flush( ofstream & os ) { 121 return fflush( (FILE *)(os.file$) ); 122 } // flush 104 bool fail( ofstream & os ) { return os.file$ == 0 || ferror( (FILE *)(os.file$) ); } 105 void clear( ofstream & os ) { clearerr( (FILE *)(os.file$) ); } 106 int flush( ofstream & os ) { return fflush( (FILE *)(os.file$) ); } 123 107 124 108 void open( ofstream & os, const char name[], const char mode[] ) { 125 FILE * file = fopen( name, mode ); 126 #ifdef __CFA_DEBUG__ 109 FILE * file; 110 for ( cnt; 10 ) { 111 errno = 0; 112 file = fopen( name, mode ); 113 if ( file != 0p || errno != EINTR ) break; // timer interrupt ? 114 if ( cnt == 9 ) abort( "ofstream open EINTR spinning exceeded" ); 115 } // for 127 116 if ( file == 0p ) { 128 117 throw (Open_Failure){ os }; 129 118 // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno ); 130 119 } // if 131 #endif // __CFA_DEBUG__ 132 (os){ file }; 120 (os){ file }; // initialize 133 121 } // open 134 122 135 void open( ofstream & os, const char name[] ) { 136 open( os, name, "w" ); 137 } // open 138 139 void close( ofstream & os ) { 140 if ( (FILE *)(os.file$) == 0p ) return; 141 if ( (FILE *)(os.file$) == (FILE *)stdout || (FILE *)(os.file$) == (FILE *)stderr ) return; 142 143 if ( fclose( (FILE *)(os.file$) ) == EOF ) { 123 void open( ofstream & os, const char name[] ) { open( os, name, "w" ); } 124 125 void close( ofstream & os ) with( os ) { 126 if ( (FILE *)(file$) == 0p ) return; 127 if ( (FILE *)(file$) == (FILE *)stdout || (FILE *)(file$) == (FILE *)stderr ) return; 128 129 int ret; 130 for ( cnt; 10 ) { 131 errno = 0; 132 ret = fclose( (FILE *)(file$) ); 133 if ( ret != EOF || errno != EINTR ) break; // timer interrupt ? 134 if ( cnt == 9 ) abort( "ofstream open EINTR spinning exceeded" ); 135 } // for 136 if ( ret == EOF ) { 144 137 throw (Close_Failure){ os }; 145 138 // abort | IO_MSG "close output" | nl | strerror( errno ); 146 139 } // if 147 os.file$ = 0p;140 file$ = 0p; // safety after close 148 141 } // close 149 142 … … 164 157 va_list args; 165 158 va_start( args, format ); 166 int len = vfprintf( (FILE *)(os.file$), format, args ); 159 160 int len; 161 for ( cnt; 10 ) { 162 errno = 0; 163 len = vfprintf( (FILE *)(os.file$), format, args ); 164 if ( len != EOF || errno != EINTR ) break; // timer interrupt ? 165 if ( cnt == 9 ) abort( "ofstream fmt EINTR spinning exceeded" ); 166 } // for 167 167 if ( len == EOF ) { 168 168 if ( ferror( (FILE *)(os.file$) ) ) { … … 177 177 } // fmt 178 178 179 inline void acquire( ofstream & os ) {180 lock( os.lock$ );181 if ( ! os.acquired$ ) os.acquired$ = true;182 else unlock( os.lock$ );183 } // acquire184 185 inline void release( ofstream & os ) {186 unlock( os.lock$ );187 } // release188 189 void ?{}( osacquire & acq, ofstream & os ) { &acq.os = &os; lock( os.lock$ ); }190 void ^?{}( osacquire & acq ) { release( acq.os ); }191 192 179 static ofstream soutFile = { (FILE *)stdout }; 193 180 ofstream & sout = soutFile, & stdout = soutFile; … … 207 194 flush( os ); 208 195 return os; 209 // (ofstream &)(os | '\n');210 // setPrt$( os, false ); // turn off211 // setNL$( os, true );212 // flush( os );213 // return sepOff( os ); // prepare for next line214 196 } // nl 215 197 … … 219 201 220 202 // private 221 void ?{}( ifstream & is, void * file ) { 222 is.file$ = file; 223 is.nlOnOff$ = false; 224 is.acquired$ = false; 225 } // ?{} 203 void ?{}( ifstream & is, void * file ) with( is ) { 204 file$ = file; 205 nlOnOff$ = false; 206 } // ?{} 207 208 bool getANL$( ifstream & os ) { return os.nlOnOff$; } 209 210 inline void lock( ifstream & os ) with( os ) { lock( os.lock$ ); } 211 inline void unlock( ifstream & os ) { unlock( os.lock$ ); } 226 212 227 213 // public 228 214 void ?{}( ifstream & is ) { is.file$ = 0p; } 229 230 void ?{}( ifstream & is, const char name[], const char mode[] ) { 231 open( is, name, mode ); 232 } // ?{} 233 234 void ?{}( ifstream & is, const char name[] ) { 235 open( is, name, "r" ); 236 } // ?{} 237 238 void ^?{}( ifstream & is ) { 239 close( is ); 240 } // ^?{} 215 void ?{}( ifstream & is, const char name[], const char mode[] ) { open( is, name, mode ); } 216 void ?{}( ifstream & is, const char name[] ) { open( is, name, "r" ); } 217 void ^?{}( ifstream & is ) { close( is ); } 218 219 bool fail( ifstream & is ) { return is.file$ == 0p || ferror( (FILE *)(is.file$) ); } 220 void clear( ifstream & is ) { clearerr( (FILE *)(is.file$) ); } 241 221 242 222 void nlOn( ifstream & os ) { os.nlOnOff$ = true; } 243 223 void nlOff( ifstream & os ) { os.nlOnOff$ = false; } 244 bool getANL( ifstream & os ) { return os.nlOnOff$; } 245 246 bool fail( ifstream & is ) { 247 return is.file$ == 0p || ferror( (FILE *)(is.file$) ); 248 } // fail 249 250 void clear( ifstream & is ) { 251 clearerr( (FILE *)(is.file$) ); 252 } // clear 253 254 void ends( ifstream & is ) { 255 if ( is.acquired$ ) { is.acquired$ = false; release( is ); } 256 } // ends 257 258 bool eof( ifstream & is ) { 259 return feof( (FILE *)(is.file$) ); 260 } // eof 224 225 void ends( ifstream & is ) {} 226 227 bool eof( ifstream & is ) { return feof( (FILE *)(is.file$) ) != 0; } 261 228 262 229 void open( ifstream & is, const char name[], const char mode[] ) { 263 FILE * file = fopen( name, mode ); 264 #ifdef __CFA_DEBUG__ 230 FILE * file; 231 for ( cnt; 10 ) { 232 errno = 0; 233 file = fopen( name, mode ); 234 if ( file != 0p || errno != EINTR ) break; // timer interrupt ? 235 if ( cnt == 9 ) abort( "ifstream open EINTR spinning exceeded" ); 236 } // for 265 237 if ( file == 0p ) { 266 238 throw (Open_Failure){ is }; 267 239 // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno ); 268 240 } // if 269 #endif // __CFA_DEBUG__ 270 is.file$ = file; 241 (is){ file }; // initialize 271 242 } // open 272 243 273 void open( ifstream & is, const char name[] ) { 274 open( is, name, "r" ); 275 } // open 276 277 void close( ifstream & is ) { 278 if ( (FILE *)(is.file$) == 0p ) return; 279 if ( (FILE *)(is.file$) == (FILE *)stdin ) return; 280 281 if ( fclose( (FILE *)(is.file$) ) == EOF ) { 244 void open( ifstream & is, const char name[] ) { open( is, name, "r" ); } 245 246 void close( ifstream & is ) with( is ) { 247 if ( (FILE *)(file$) == 0p ) return; 248 if ( (FILE *)(file$) == (FILE *)stdin ) return; 249 250 int ret; 251 for ( cnt; 10 ) { 252 errno = 0; 253 ret = fclose( (FILE *)(file$) ); 254 if ( ret != EOF || errno != EINTR ) break; // timer interrupt ? 255 if ( cnt == 9 ) abort( "ifstream close EINTR spinning exceeded" ); 256 } // for 257 if ( ret == EOF ) { 282 258 throw (Close_Failure){ is }; 283 259 // abort | IO_MSG "close input" | nl | strerror( errno ); 284 260 } // if 285 is.file$ = 0p;261 file$ = 0p; // safety after close 286 262 } // close 287 263 … … 312 288 int fmt( ifstream & is, const char format[], ... ) { 313 289 va_list args; 314 315 290 va_start( args, format ); 316 int len = vfscanf( (FILE *)(is.file$), format, args ); 291 292 int len; 293 for () { // no check for EINTR limit waiting for keyboard input 294 errno = 0; 295 len = vfscanf( (FILE *)(is.file$), format, args ); 296 if ( len != EOF || errno != EINTR ) break; // timer interrupt ? 297 } // for 317 298 if ( len == EOF ) { 318 299 if ( ferror( (FILE *)(is.file$) ) ) { … … 324 305 } // fmt 325 306 326 inline void acquire( ifstream & is ) {327 lock( is.lock$ );328 if ( ! is.acquired$ ) is.acquired$ = true;329 else unlock( is.lock$ );330 } // acquire331 332 inline void release( ifstream & is ) {333 unlock( is.lock$ );334 } // release335 336 void ?{}( isacquire & acq, ifstream & is ) { &acq.is = &is; lock( is.lock$ ); }337 void ^?{}( isacquire & acq ) { release( acq.is ); }338 339 307 static ifstream sinFile = { (FILE *)stdin }; 340 308 ifstream & sin = sinFile, & stdin = sinFile; … … 347 315 348 316 // exception I/O constructors 349 void ?{}( Open_Failure & this, ofstream & ostream) {350 this.virtual_table = &Open_Failure_vt;351 this.ostream = &ostream;352 t his.tag = 1;353 } // ?{} 354 355 void ?{}( Open_Failure & this, ifstream & istream) {356 this.virtual_table = &Open_Failure_vt;357 this.istream = &istream;358 t his.tag = 0;317 void ?{}( Open_Failure & ex, ofstream & ostream ) with(ex) { 318 virtual_table = &Open_Failure_vt; 319 ostream = &ostream; 320 tag = 1; 321 } // ?{} 322 323 void ?{}( Open_Failure & ex, ifstream & istream ) with(ex) { 324 virtual_table = &Open_Failure_vt; 325 istream = &istream; 326 tag = 0; 359 327 } // ?{} 360 328 … … 363 331 364 332 // exception I/O constructors 365 void ?{}( Close_Failure & this, ofstream & ostream) {366 this.virtual_table = &Close_Failure_vt;367 this.ostream = &ostream;368 t his.tag = 1;369 } // ?{} 370 371 void ?{}( Close_Failure & this, ifstream & istream) {372 this.virtual_table = &Close_Failure_vt;373 this.istream = &istream;374 t his.tag = 0;333 void ?{}( Close_Failure & ex, ofstream & ostream ) with(ex) { 334 virtual_table = &Close_Failure_vt; 335 ostream = &ostream; 336 tag = 1; 337 } // ?{} 338 339 void ?{}( Close_Failure & ex, ifstream & istream ) with(ex) { 340 virtual_table = &Close_Failure_vt; 341 istream = &istream; 342 tag = 0; 375 343 } // ?{} 376 344 … … 379 347 380 348 // exception I/O constructors 381 void ?{}( Write_Failure & this, ofstream & ostream) {382 this.virtual_table = &Write_Failure_vt;383 this.ostream = &ostream;384 t his.tag = 1;385 } // ?{} 386 387 void ?{}( Write_Failure & this, ifstream & istream) {388 this.virtual_table = &Write_Failure_vt;389 this.istream = &istream;390 t his.tag = 0;349 void ?{}( Write_Failure & ex, ofstream & ostream ) with(ex) { 350 virtual_table = &Write_Failure_vt; 351 ostream = &ostream; 352 tag = 1; 353 } // ?{} 354 355 void ?{}( Write_Failure & ex, ifstream & istream ) with(ex) { 356 virtual_table = &Write_Failure_vt; 357 istream = &istream; 358 tag = 0; 391 359 } // ?{} 392 360 … … 395 363 396 364 // exception I/O constructors 397 void ?{}( Read_Failure & this, ofstream & ostream) {398 this.virtual_table = &Read_Failure_vt;399 this.ostream = &ostream;400 t his.tag = 1;401 } // ?{} 402 403 void ?{}( Read_Failure & this, ifstream & istream) {404 this.virtual_table = &Read_Failure_vt;405 this.istream = &istream;406 t his.tag = 0;365 void ?{}( Read_Failure & ex, ofstream & ostream ) with(ex) { 366 virtual_table = &Read_Failure_vt; 367 ostream = &ostream; 368 tag = 1; 369 } // ?{} 370 371 void ?{}( Read_Failure & ex, ifstream & istream ) with(ex) { 372 virtual_table = &Read_Failure_vt; 373 istream = &istream; 374 tag = 0; 407 375 } // ?{} 408 376 -
libcfa/src/fstream.hfa
rf95634e rb7fd9daf 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jul 28 07:35:50202113 // Update Count : 2 3412 // Last Modified On : Sun Oct 10 09:37:32 2021 13 // Update Count : 243 14 14 // 15 15 … … 36 36 char tupleSeparator$[ofstream_sepSize]; 37 37 multiple_acquisition_lock lock$; 38 bool acquired$;39 38 }; // ofstream 40 39 … … 52 51 bool getPrt$( ofstream & ); 53 52 void setPrt$( ofstream &, bool ); 53 54 void lock( ofstream & ); 55 void unlock( ofstream & ); 54 56 55 57 // public … … 75 77 void open( ofstream &, const char name[] ); 76 78 void close( ofstream & ); 79 77 80 ofstream & write( ofstream &, const char data[], size_t size ); 78 79 void acquire( ofstream & );80 void release( ofstream & );81 82 struct osacquire {83 ofstream & os;84 };85 void ?{}( osacquire & acq, ofstream & );86 void ^?{}( osacquire & acq );87 81 88 82 void ?{}( ofstream & ); … … 107 101 bool nlOnOff$; 108 102 multiple_acquisition_lock lock$; 109 bool acquired$;110 103 }; // ifstream 111 104 112 105 // Satisfies istream 113 106 107 // private 108 bool getANL$( ifstream & ); 109 110 void lock( ifstream & ); 111 void unlock( ifstream & ); 112 114 113 // public 115 114 void nlOn( ifstream & ); 116 115 void nlOff( ifstream & ); 117 bool getANL( ifstream & );118 116 void ends( ifstream & ); 119 117 int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); … … 125 123 void open( ifstream & is, const char name[] ); 126 124 void close( ifstream & is ); 125 127 126 ifstream & read( ifstream & is, char data[], size_t size ); 128 127 ifstream & ungetc( ifstream & is, char c ); 129 130 void acquire( ifstream & is );131 void release( ifstream & is );132 133 struct isacquire {134 ifstream & is;135 };136 void ?{}( isacquire & acq, ifstream & is );137 void ^?{}( isacquire & acq );138 128 139 129 void ?{}( ifstream & is ); -
libcfa/src/heap.cfa
rf95634e rb7fd9daf 102 102 } // prtUnfreed 103 103 104 extern int cfa_main_returned; // from bootloader.cf104 extern int cfa_main_returned; // from interpose.cfa 105 105 extern "C" { 106 106 void heapAppStart() { // called by __cfaabi_appready_startup -
libcfa/src/interpose.cfa
rf95634e rb7fd9daf 94 94 } __cabi_libc; 95 95 96 int cfa_main_returned; 97 96 98 extern "C" { 97 99 void __cfaabi_interpose_startup( void ) { 98 100 const char *version = 0p; 101 cfa_main_returned = 0; 99 102 100 103 preload_libgcc(); -
libcfa/src/iostream.cfa
rf95634e rb7fd9daf 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : S at May 15 09:39:21202113 // Update Count : 134 212 // Last Modified On : Sun Oct 10 09:28:17 2021 13 // Update Count : 1345 14 14 // 15 15 … … 398 398 return os; 399 399 } // nlOff 400 } // distribution401 402 forall( ostype & | ostream( ostype ) ) {403 ostype & acquire( ostype & os ) {404 acquire( os ); // call void returning405 return os;406 } // acquire407 400 } // distribution 408 401 … … 829 822 fmt( is, "%c", &temp ); // must pass pointer through varg to fmt 830 823 // do not overwrite parameter with newline unless appropriate 831 if ( temp != '\n' || getANL ( is ) ) { c = temp; break; }824 if ( temp != '\n' || getANL$( is ) ) { c = temp; break; } 832 825 if ( eof( is ) ) break; 833 826 } // for … … 1035 1028 return is; 1036 1029 } // nlOff 1037 } // distribution1038 1039 forall( istype & | istream( istype ) ) {1040 istype & acquire( istype & is ) {1041 acquire( is ); // call void returning1042 return is;1043 } // acquire1044 1030 } // distribution 1045 1031 -
libcfa/src/iostream.hfa
rf95634e rb7fd9daf 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Apr 28 20:37:56202113 // Update Count : 40 112 // Last Modified On : Sun Oct 10 10:02:07 2021 13 // Update Count : 407 14 14 // 15 15 … … 58 58 void close( ostype & ); 59 59 ostype & write( ostype &, const char [], size_t ); 60 void acquire( ostype & ); // concurrent access61 60 }; // ostream 62 61 … … 142 141 ostype & nlOn( ostype & ); 143 142 ostype & nlOff( ostype & ); 144 } // distribution145 146 forall( ostype & | ostream( ostype ) ) {147 ostype & acquire( ostype & );148 143 } // distribution 149 144 … … 296 291 297 292 trait basic_istream( istype & ) { 298 bool getANL( istype & ); // get scan newline (on/off) 293 // private 294 bool getANL$( istype & ); // get scan newline (on/off) 295 // public 299 296 void nlOn( istype & ); // read newline 300 297 void nlOff( istype & ); // scan newline 301 302 298 void ends( istype & os ); // end of output statement 303 299 int fmt( istype &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); … … 312 308 void close( istype & is ); 313 309 istype & read( istype &, char [], size_t ); 314 void acquire( istype & ); // concurrent access315 310 }; // istream 316 311 … … 379 374 } // distribution 380 375 381 forall( istype & | istream( istype ) ) {382 istype & acquire( istype & );383 } // distribution384 385 376 // *********************************** manipulators *********************************** 386 377 -
libcfa/src/memory.cfa
rf95634e rb7fd9daf 155 155 156 156 forall(T &) 157 T * release(unique_ptr(T) & this) { 158 T * data = this.data; 159 this.data = 0p; 160 return data; 161 } 162 163 forall(T &) 157 164 int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that) { 158 165 return this.data == that.data; -
libcfa/src/memory.hfa
rf95634e rb7fd9daf 94 94 95 95 forall(T &) 96 T * release(unique_ptr(T) & this); 97 98 forall(T &) 96 99 int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that); 97 100 forall(T &) -
libcfa/src/strstream.cfa
rf95634e rb7fd9daf 10 10 // Created On : Thu Apr 22 22:24:35 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Apr 27 20:59:53202113 // Update Count : 7812 // Last Modified On : Sun Oct 10 16:13:20 2021 13 // Update Count : 101 14 14 // 15 15 16 16 #include "strstream.hfa" 17 #include "fstream.hfa" // abort 17 18 18 19 #include <stdio.h> // vsnprintf … … 30 31 31 32 // private 32 bool sepPrt$( ostrstream & os ) { setNL$( os, false ); return os.sepOnOff$; }33 void sepReset$( ostrstream & os ) { os.sepOnOff$ = os.sepDefault$; }34 void sepReset$( ostrstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }35 const char * sepGetCur$( ostrstream & os ) { return os.sepCur$; }36 void sepSetCur$( ostrstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }37 bool getNL$( ostrstream & os ) { return os.sawNL$; }38 void setNL$( ostrstream & os, bool state ) { os.sawNL$ = state; }39 bool getANL$( ostrstream & os ) { return os.nlOnOff$; }40 bool getPrt$( ostrstream & os ) { return os.prt$; }41 void setPrt$( ostrstream & os, bool state ) { os.prt$ = state; }33 inline bool sepPrt$( ostrstream & os ) { setNL$( os, false ); return os.sepOnOff$; } 34 inline void sepReset$( ostrstream & os ) { os.sepOnOff$ = os.sepDefault$; } 35 inline void sepReset$( ostrstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; } 36 inline const char * sepGetCur$( ostrstream & os ) { return os.sepCur$; } 37 inline void sepSetCur$( ostrstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; } 38 inline bool getNL$( ostrstream & os ) { return os.sawNL$; } 39 inline void setNL$( ostrstream & os, bool state ) { os.sawNL$ = state; } 40 inline bool getANL$( ostrstream & os ) { return os.nlOnOff$; } 41 inline bool getPrt$( ostrstream & os ) { return os.prt$; } 42 inline void setPrt$( ostrstream & os, bool state ) { os.prt$ = state; } 42 43 43 44 // public … … 128 129 // *********************************** istrstream *********************************** 129 130 131 // private 132 bool getANL$( istrstream & is ) { return is.nlOnOff$; } 130 133 131 134 // public … … 136 139 } // ?{} 137 140 138 bool getANL( istrstream & is ) { return is.nlOnOff$; }139 141 void nlOn( istrstream & is ) { is.nlOnOff$ = true; } 140 142 void nlOff( istrstream & is ) { is.nlOnOff$ = false; } 141 143 142 void ends( istrstream & is ) { 143 } // ends 144 void ends( istrstream & is ) {} 145 bool eof( istrstream & is ) { return false; } 144 146 145 int eof( istrstream & is ) { 146 return 0; 147 } // eof 147 int fmt( istrstream & is, const char format[], ... ) with(is) { 148 va_list args; 149 va_start( args, format ); 150 // THIS DOES NOT WORK BECAUSE VSSCANF RETURNS NUMBER OF VALUES READ VERSUS BUFFER POSITION SCANNED. 151 int len = vsscanf( buf$ + cursor$, format, args ); 152 va_end( args ); 153 if ( len == EOF ) { 154 abort | IO_MSG "invalid read"; 155 } // if 156 // SKULLDUGGERY: This hack skips over characters read by vsscanf by moving to the next whitespace but it does not 157 // handle C reads with wdi manipulators that leave the cursor at a non-whitespace character. 158 for ( ; buf$[cursor$] != ' ' && buf$[cursor$] != '\t' && buf$[cursor$] != '\0'; cursor$ += 1 ) { 159 //printf( "X \'%c\'\n", buf$[cursor$] ); 160 } // for 161 if ( buf$[cursor$] != '\0' ) cursor$ += 1; // advance to whitespace 162 return len; 163 } // fmt 148 164 149 165 istrstream &ungetc( istrstream & is, char c ) { … … 154 170 } // ungetc 155 171 156 int fmt( istrstream & is, const char format[], ... ) {157 va_list args;158 va_start( args, format );159 // This does not work because vsscanf does not return buffer position.160 int len = vsscanf( is.buf$ + is.cursor$, format, args );161 va_end( args );162 if ( len == EOF ) {163 int j;164 printf( "X %d%n\n", len, &j );165 } // if166 is.cursor$ += len;167 return len;168 } // fmt169 170 172 // Local Variables: // 171 173 // tab-width: 4 // -
libcfa/src/strstream.hfa
rf95634e rb7fd9daf 10 10 // Created On : Thu Apr 22 22:20:59 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Apr 27 20:58:50202113 // Update Count : 4 112 // Last Modified On : Sun Oct 10 10:14:22 2021 13 // Update Count : 47 14 14 // 15 15 … … 85 85 // Satisfies basic_istream 86 86 87 // private 88 bool getANL$( istrstream & ); 89 87 90 // public 88 bool getANL( istrstream & );89 91 void nlOn( istrstream & ); 90 92 void nlOff( istrstream & ); 91 93 void ends( istrstream & ); 94 92 95 int fmt( istrstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 93 istrstream & ungetc( istrstream & is, char c);94 int eof( istrstream & is);96 istrstream & ungetc( istrstream &, char ); 97 bool eof( istrstream & ); 95 98 96 void ?{}( istrstream & is, char buf[] );99 void ?{}( istrstream &, char buf[] ); 97 100 98 101 // Local Variables: // -
src/AST/Decl.hpp
rf95634e rb7fd9daf 131 131 // declared type, derived from parameter declarations 132 132 ptr<FunctionType> type; 133 /// Null for the forward declaration of a function. 133 134 ptr<CompoundStmt> stmts; 134 135 std::vector< ptr<Expr> > withExprs; -
src/AST/Pass.hpp
rf95634e rb7fd9daf 348 348 349 349 /// When this node is finished being visited, restore the value of a variable 350 /// You may assign to the return value to set the new value in the same statement. 350 351 template< typename T > 351 voidGuardValue( T& val ) {352 T& GuardValue( T& val ) { 352 353 at_cleanup( [ val ]( void * newVal ) { 353 354 * static_cast< T * >( newVal ) = val; 354 355 }, static_cast< void * >( & val ) ); 356 return val; 355 357 } 356 358 … … 394 396 }; 395 397 398 /// Used to get a pointer to the wrapping TranslationUnit. 399 struct WithConstTranslationUnit { 400 const TranslationUnit * translationUnit = nullptr; 401 402 const TranslationUnit & transUnit() const { 403 assertf( translationUnit, "WithConstTranslationUnit not set-up." ); 404 return *translationUnit; 405 } 406 }; 407 396 408 } 397 409 -
src/AST/Pass.impl.hpp
rf95634e rb7fd9daf 420 420 template< typename core_t > 421 421 inline void ast::accept_all( ast::TranslationUnit & unit, ast::Pass< core_t > & visitor ) { 422 return ast::accept_all( unit.decls, visitor ); 422 if ( auto ptr = __pass::translation_unit::get_cptr( visitor.core, 0 ) ) { 423 ValueGuard<const TranslationUnit *> guard( *ptr ); 424 *ptr = &unit; 425 return ast::accept_all( unit.decls, visitor ); 426 } else { 427 return ast::accept_all( unit.decls, visitor ); 428 } 423 429 } 424 430 -
src/AST/Pass.proto.hpp
rf95634e rb7fd9daf 426 426 } // namespace forall 427 427 428 // For passes that need access to the global context. Sreaches `translationUnit` 429 namespace translation_unit { 430 template<typename core_t> 431 static inline auto get_cptr( core_t & core, int ) 432 -> decltype( &core.translationUnit ) { 433 return &core.translationUnit; 434 } 435 436 template<typename core_t> 437 static inline const TranslationUnit ** get_cptr( core_t &, long ) { 438 return nullptr; 439 } 440 } 441 428 442 template<typename core_t> 429 443 static inline auto get_result( core_t & core, char ) -> decltype( core.result() ) { -
src/AST/Stmt.hpp
rf95634e rb7fd9daf 175 175 class CaseStmt final : public Stmt { 176 176 public: 177 /// Null for the default label. 177 178 ptr<Expr> cond; 178 179 std::vector<ptr<Stmt>> stmts; -
src/AST/TranslationUnit.hpp
rf95634e rb7fd9daf 26 26 std::list< ptr< Decl > > decls; 27 27 28 struct Global s{28 struct Global { 29 29 std::map< UniqueId, Decl * > idMap; 30 30 31 const Type *sizeType;31 ptr<Type> sizeType; 32 32 const FunctionDecl * dereference; 33 33 const StructDecl * dtorStruct; -
src/AST/porting.md
rf95634e rb7fd9daf 98 98 * `Initializer` => `ast::Init` 99 99 * `Statement` => `ast::Stmt` 100 * `ReferenceToType` => `ast::BaseInstType` 100 101 * any field names should follow a similar renaming 101 102 * because they don't really belong to `Type` (and for consistency with `Linkage::Spec`): -
src/CodeGen/FixMain.cc
rf95634e rb7fd9daf 22 22 #include <string> // for operator<< 23 23 24 #include "AST/Decl.hpp" 25 #include "AST/Type.hpp" 26 #include "Common/PassVisitor.h" 24 27 #include "Common/SemanticError.h" // for SemanticError 25 28 #include "CodeGen/GenType.h" // for GenType … … 29 32 30 33 namespace CodeGen { 34 35 namespace { 36 37 struct FindMainCore { 38 FunctionDecl * main_signature = nullptr; 39 40 void previsit( FunctionDecl * decl ) { 41 if ( FixMain::isMain( decl ) ) { 42 if ( main_signature ) { 43 SemanticError( decl, "Multiple definition of main routine\n" ); 44 } 45 main_signature = decl; 46 } 47 } 48 }; 49 50 } 51 31 52 bool FixMain::replace_main = false; 32 std::unique_ptr<FunctionDecl> FixMain::main_signature = nullptr;33 53 34 54 template<typename container> … … 37 57 } 38 58 39 void FixMain::registerMain(FunctionDecl* functionDecl) 40 { 41 if(main_signature) { 42 SemanticError(functionDecl, "Multiple definition of main routine\n"); 43 } 44 main_signature.reset( functionDecl->clone() ); 45 } 59 void FixMain::fix( std::list< Declaration * > & translationUnit, 60 std::ostream &os, const char* bootloader_filename ) { 61 PassVisitor< FindMainCore > main_finder; 62 acceptAll( translationUnit, main_finder ); 63 FunctionDecl * main_signature = main_finder.pass.main_signature; 46 64 47 void FixMain::fix(std::ostream &os, const char* bootloader_filename) {48 65 if( main_signature ) { 49 66 os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return "; 50 main_signature->mangleName = SymTab::Mangler::mangle(main_signature .get());67 main_signature->mangleName = SymTab::Mangler::mangle(main_signature); 51 68 52 69 os << main_signature->get_scopedMangleName() << "("; … … 65 82 } 66 83 } 84 85 namespace { 86 87 ObjectDecl * signedIntObj() { 88 return new ObjectDecl( 89 "", Type::StorageClasses(), LinkageSpec::Cforall, 0, 90 new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr ); 91 } 92 93 ObjectDecl * charStarObj() { 94 return new ObjectDecl( 95 "", Type::StorageClasses(), LinkageSpec::Cforall, 0, 96 new PointerType( Type::Qualifiers(), 97 new PointerType( Type::Qualifiers(), 98 new BasicType( Type::Qualifiers(), BasicType::Char ) ) ), 99 nullptr ); 100 } 101 102 std::string create_mangled_main_function_name( FunctionType * function_type ) { 103 std::unique_ptr<FunctionDecl> decl( new FunctionDecl( 104 "main", Type::StorageClasses(), LinkageSpec::Cforall, 105 function_type, nullptr ) ); 106 return SymTab::Mangler::mangle( decl.get() ); 107 } 108 109 std::string mangled_0_argument_main() { 110 FunctionType* main_type = new FunctionType( Type::Qualifiers(), true ); 111 main_type->get_returnVals().push_back( signedIntObj() ); 112 return create_mangled_main_function_name( main_type ); 113 } 114 115 std::string mangled_2_argument_main() { 116 FunctionType* main_type = new FunctionType( Type::Qualifiers(), false ); 117 main_type->get_returnVals().push_back( signedIntObj() ); 118 main_type->get_parameters().push_back( signedIntObj() ); 119 main_type->get_parameters().push_back( charStarObj() ); 120 return create_mangled_main_function_name( main_type ); 121 } 122 123 bool is_main( const std::string & mangled_name ) { 124 // This breaks if you move it out of the function. 125 static const std::string mangled_mains[] = { 126 mangled_0_argument_main(), 127 mangled_2_argument_main(), 128 //mangled_3_argument_main(), 129 }; 130 131 for ( auto main_name : mangled_mains ) { 132 if ( main_name == mangled_name ) return true; 133 } 134 return false; 135 } 136 137 } // namespace 138 139 bool FixMain::isMain( FunctionDecl * decl ) { 140 if ( std::string("main") != decl->name ) { 141 return false; 142 } 143 return is_main( SymTab::Mangler::mangle( decl, true, true ) ); 144 } 145 146 bool FixMain::isMain( const ast::FunctionDecl * decl ) { 147 if ( std::string("main") != decl->name ) { 148 return false; 149 } 150 return is_main( Mangle::mangle( decl, Mangle::Type ) ); 151 } 152 67 153 }; -
src/CodeGen/FixMain.h
rf95634e rb7fd9daf 9 9 // Author : Thierry Delisle 10 10 // Created On : Thr Jan 12 14:11:09 2017 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sun Feb 16 03:24:32 202013 // Update Count : 511 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Oct 29 16:20:00 2021 13 // Update Count : 8 14 14 // 15 15 … … 18 18 #include <iosfwd> 19 19 #include <memory> 20 #include <list> 20 21 21 22 #include "SynTree/LinkageSpec.h" 22 23 24 class Declaration; 23 25 class FunctionDecl; 26 namespace ast { 27 class FunctionDecl; 28 } 24 29 25 30 namespace CodeGen { 26 class FixMain {27 public :28 static inline LinkageSpec::Spec mainLinkage() {29 return replace_main ? LinkageSpec::Cforall : LinkageSpec::C;30 }31 32 static inline void setReplaceMain(bool val) {33 replace_main = val;34 }35 31 36 static void registerMain(FunctionDecl* val); 32 class FixMain { 33 public : 34 static inline LinkageSpec::Spec mainLinkage() { 35 return replace_main ? LinkageSpec::Cforall : LinkageSpec::C; 36 } 37 37 38 static void fix(std::ostream &os, const char* bootloader_filename); 38 static inline void setReplaceMain(bool val) { 39 replace_main = val; 40 } 39 41 40 private: 41 static bool replace_main; 42 static std::unique_ptr<FunctionDecl> main_signature; 43 }; 42 static bool isMain(FunctionDecl* decl); 43 static bool isMain(const ast::FunctionDecl * decl); 44 45 static void fix( std::list< Declaration * > & decls, 46 std::ostream &os, const char* bootloader_filename ); 47 48 private: 49 static bool replace_main; 50 }; 51 44 52 } // namespace CodeGen -
src/CodeGen/FixNames.cc
rf95634e rb7fd9daf 9 9 // Author : Richard C. Bilson 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Fri Dec 13 23:39:14 201913 // Update Count : 2 111 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Oct 29 15:49:00 2021 13 // Update Count : 23 14 14 // 15 15 … … 19 19 #include <string> // for string, operator!=, operator== 20 20 21 #include "AST/Chain.hpp" 22 #include "AST/Expr.hpp" 23 #include "AST/Pass.hpp" 21 24 #include "Common/PassVisitor.h" 22 25 #include "Common/SemanticError.h" // for SemanticError … … 46 49 }; 47 50 48 std::string mangle_main() {49 FunctionType* main_type;50 std::unique_ptr<FunctionDecl> mainDecl { new FunctionDecl( "main", Type::StorageClasses(), LinkageSpec::Cforall,51 main_type = new FunctionType( Type::Qualifiers(), true ), nullptr )52 };53 main_type->get_returnVals().push_back(54 new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )55 );56 57 auto && name = SymTab::Mangler::mangle( mainDecl.get() );58 // std::cerr << name << std::endl;59 return std::move(name);60 }61 std::string mangle_main_args() {62 FunctionType* main_type;63 std::unique_ptr<FunctionDecl> mainDecl { new FunctionDecl( "main", Type::StorageClasses(), LinkageSpec::Cforall,64 main_type = new FunctionType( Type::Qualifiers(), false ), nullptr )65 };66 main_type->get_returnVals().push_back(67 new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )68 );69 70 main_type->get_parameters().push_back(71 new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )72 );73 74 main_type->get_parameters().push_back(75 new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0,76 new PointerType( Type::Qualifiers(), new PointerType( Type::Qualifiers(), new BasicType( Type::Qualifiers(), BasicType::Char ) ) ),77 nullptr )78 );79 80 auto&& name = SymTab::Mangler::mangle( mainDecl.get() );81 // std::cerr << name << std::endl;82 return std::move(name);83 }84 85 bool is_main(const std::string& name) {86 static std::string mains[] = {87 mangle_main(),88 mangle_main_args()89 };90 91 for(const auto& m : mains) {92 if( name == m ) return true;93 }94 return false;95 }96 97 51 void fixNames( std::list< Declaration* > & translationUnit ) { 98 52 PassVisitor<FixNames> fixer; … … 118 72 fixDWT( functionDecl ); 119 73 120 if (is_main( SymTab::Mangler::mangle(functionDecl, true, true) )) {74 if ( FixMain::isMain( functionDecl ) ) { 121 75 int nargs = functionDecl->get_functionType()->get_parameters().size(); 122 76 if( !(nargs == 0 || nargs == 2 || nargs == 3) ) { … … 124 78 } 125 79 functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( new ConstantExpr( Constant::from_int( 0 ) ) ) ); 126 CodeGen::FixMain::registerMain( functionDecl );127 80 } 128 81 } … … 132 85 GuardAction( [this](){ scopeLevel--; } ); 133 86 } 87 88 /// Does work with the main function and scopeLevels. 89 class FixNames_new : public ast::WithGuards { 90 int scopeLevel = 1; 91 92 bool shouldSetScopeLevel( const ast::DeclWithType * dwt ) { 93 return !dwt->name.empty() && dwt->linkage.is_mangled 94 && dwt->scopeLevel != scopeLevel; 95 } 96 public: 97 const ast::ObjectDecl *postvisit( const ast::ObjectDecl *objectDecl ) { 98 if ( shouldSetScopeLevel( objectDecl ) ) { 99 return ast::mutate_field( objectDecl, &ast::ObjectDecl::scopeLevel, scopeLevel ); 100 } 101 return objectDecl; 102 } 103 104 const ast::FunctionDecl *postvisit( const ast::FunctionDecl *functionDecl ) { 105 // This store is used to ensure a maximum of one call to mutate. 106 ast::FunctionDecl * mutDecl = nullptr; 107 108 if ( shouldSetScopeLevel( functionDecl ) ) { 109 mutDecl = ast::mutate( functionDecl ); 110 mutDecl->scopeLevel = scopeLevel; 111 } 112 113 if ( FixMain::isMain( functionDecl ) ) { 114 if ( !mutDecl ) { mutDecl = ast::mutate( functionDecl ); } 115 116 int nargs = mutDecl->params.size(); 117 if ( 0 != nargs && 2 != nargs && 3 != nargs ) { 118 SemanticError( functionDecl, "Main expected to have 0, 2 or 3 arguments\n" ); 119 } 120 ast::chain_mutate( mutDecl->stmts )->kids.push_back( 121 new ast::ReturnStmt( 122 mutDecl->location, 123 ast::ConstantExpr::from_int( mutDecl->location, 0 ) 124 ) 125 ); 126 } 127 return mutDecl ? mutDecl : functionDecl; 128 } 129 130 void previsit( const ast::CompoundStmt * ) { 131 GuardValue( scopeLevel ) += 1; 132 } 133 }; 134 135 void fixNames( ast::TranslationUnit & translationUnit ) { 136 ast::Pass<FixNames_new>::run( translationUnit ); 137 } 138 134 139 } // namespace CodeGen 135 140 -
src/CodeGen/FixNames.h
rf95634e rb7fd9daf 9 9 // Author : Richard C. Bilson 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Fri Jul 21 22:17:33 201713 // Update Count : 311 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 26 13:47:00 2021 13 // Update Count : 4 14 14 // 15 15 … … 19 19 20 20 class Declaration; 21 namespace ast { 22 struct TranslationUnit; 23 } 21 24 22 25 namespace CodeGen { 23 26 /// mangles object and function names 24 27 void fixNames( std::list< Declaration* > & translationUnit ); 28 void fixNames( ast::TranslationUnit & translationUnit ); 25 29 } // namespace CodeGen 26 30 -
src/CodeTools/DeclStats.cc
rf95634e rb7fd9daf 156 156 /// number of counting bins for linkages 157 157 static const unsigned n_named_specs = 8; 158 /// map from total number of specs to bins 159 static const unsigned ind_for_linkage[16]; 158 /// Mapping function from linkage to bin. 159 static unsigned linkage_index( LinkageSpec::Spec spec ) { 160 switch ( spec ) { 161 case LinkageSpec::Intrinsic: return 0; 162 case LinkageSpec::C: return 1; 163 case LinkageSpec::Cforall: return 2; 164 case LinkageSpec::AutoGen: return 3; 165 case LinkageSpec::Compiler: return 4; 166 case LinkageSpec::BuiltinCFA: return 5; 167 case LinkageSpec::BuiltinC: return 6; 168 default: return 7; 169 } 170 } 160 171 161 172 Stats for_linkage[n_named_specs]; ///< Stores separate stats per linkage … … 366 377 const std::string& mangleName = decl->get_mangleName().empty() ? decl->name : decl->get_mangleName(); 367 378 if ( seen_names.insert( mangleName ).second ) { 368 Stats& stats = for_linkage[ ind_for_linkage[ decl->linkage ]];379 Stats& stats = for_linkage[ linkage_index( decl->linkage ) ]; 369 380 370 381 ++stats.n_decls; … … 527 538 }; 528 539 529 const unsigned DeclStats::ind_for_linkage[]530 = { 7, 7, 2, 1, 7, 7, 7, 3, 4, 7, 6, 5, 7, 7, 7, 0 };531 532 540 void printDeclStats( std::list< Declaration * > &translationUnit ) { 533 541 PassVisitor<DeclStats> stats; -
src/Common/module.mk
rf95634e rb7fd9daf 22 22 Common/CompilerError.h \ 23 23 Common/Debug.h \ 24 Common/DeclStats.hpp \ 25 Common/DeclStats.cpp \ 24 26 Common/ErrorObjects.h \ 25 27 Common/Eval.cc \ … … 33 35 Common/PassVisitor.proto.h \ 34 36 Common/PersistentMap.h \ 37 Common/ResolvProtoDump.hpp \ 38 Common/ResolvProtoDump.cpp \ 35 39 Common/ScopedMap.h \ 36 40 Common/SemanticError.cc \ -
src/Concurrency/Keywords.cc
rf95634e rb7fd9daf 93 93 ObjectDecl * addField( StructDecl * ); 94 94 void addRoutines( ObjectDecl *, FunctionDecl * ); 95 void addLockUnlockRoutines( StructDecl * ); 95 96 96 97 virtual bool is_target( StructDecl * decl ) = 0; … … 322 323 StructDecl* dtor_guard_decl = nullptr; 323 324 StructDecl* thread_guard_decl = nullptr; 325 StructDecl* lock_guard_decl = nullptr; 324 326 325 327 static std::unique_ptr< Type > generic_func; … … 463 465 } 464 466 465 466 467 void ConcurrentSueKeyword::handle( StructDecl * decl ) { 467 468 if( ! decl->body ) return; … … 479 480 FunctionDecl * func = forwardDeclare( decl ); 480 481 ObjectDecl * field = addField( decl ); 482 483 // add get_.* routine 481 484 addRoutines( field, func ); 485 // add lock/unlock routines to monitors for use by mutex stmt 486 addLockUnlockRoutines( decl ); 482 487 } 483 488 … … 612 617 } 613 618 619 // This function adds the get_.* routine body for coroutines, monitors etc 620 // after their corresponding struct has been made 614 621 void ConcurrentSueKeyword::addRoutines( ObjectDecl * field, FunctionDecl * func ) { 615 622 CompoundStmt * statement = new CompoundStmt(); … … 634 641 635 642 declsToAddAfter.push_back( get_decl ); 643 } 644 645 // Generates lock/unlock routines for monitors to be used by mutex stmts 646 void ConcurrentSueKeyword::addLockUnlockRoutines( StructDecl * decl ) { 647 // this routine will be called for all ConcurrentSueKeyword children so only continue if we are a monitor 648 if ( !decl->is_monitor() ) return; 649 650 FunctionType * lock_fn_type = new FunctionType( noQualifiers, false ); 651 FunctionType * unlock_fn_type = new FunctionType( noQualifiers, false ); 652 653 // create this ptr parameter for both routines 654 ObjectDecl * this_decl = new ObjectDecl( 655 "this", 656 noStorageClasses, 657 LinkageSpec::Cforall, 658 nullptr, 659 new ReferenceType( 660 noQualifiers, 661 new StructInstType( 662 noQualifiers, 663 decl 664 ) 665 ), 666 nullptr 667 ); 668 669 lock_fn_type->get_parameters().push_back( this_decl->clone() ); 670 unlock_fn_type->get_parameters().push_back( this_decl->clone() ); 671 fixupGenerics(lock_fn_type, decl); 672 fixupGenerics(unlock_fn_type, decl); 673 674 delete this_decl; 675 676 677 ////////////////////////////////////////////////////////////////////// 678 // The following generates this lock routine for all monitors 679 /* 680 void lock (monitor_t & this) { 681 lock(get_monitor(this)); 682 } 683 */ 684 FunctionDecl * lock_decl = new FunctionDecl( 685 "lock", 686 Type::Static, 687 LinkageSpec::Cforall, 688 lock_fn_type, 689 nullptr, 690 { }, 691 Type::Inline 692 ); 693 694 UntypedExpr * get_monitor_lock = new UntypedExpr ( 695 new NameExpr( "get_monitor" ), 696 { new VariableExpr( lock_fn_type->get_parameters().front() ) } 697 ); 698 699 CompoundStmt * lock_statement = new CompoundStmt(); 700 lock_statement->push_back( 701 new ExprStmt( 702 new UntypedExpr ( 703 new NameExpr( "lock" ), 704 { 705 get_monitor_lock 706 } 707 ) 708 ) 709 ); 710 lock_decl->set_statements( lock_statement ); 711 712 ////////////////////////////////////////////////////////////////// 713 // The following generates this routine for all monitors 714 /* 715 void unlock (monitor_t & this) { 716 unlock(get_monitor(this)); 717 } 718 */ 719 FunctionDecl * unlock_decl = new FunctionDecl( 720 "unlock", 721 Type::Static, 722 LinkageSpec::Cforall, 723 unlock_fn_type, 724 nullptr, 725 { }, 726 Type::Inline 727 ); 728 729 CompoundStmt * unlock_statement = new CompoundStmt(); 730 731 UntypedExpr * get_monitor_unlock = new UntypedExpr ( 732 new NameExpr( "get_monitor" ), 733 { new VariableExpr( unlock_fn_type->get_parameters().front() ) } 734 ); 735 736 unlock_statement->push_back( 737 new ExprStmt( 738 new UntypedExpr( 739 new NameExpr( "unlock" ), 740 { 741 get_monitor_unlock 742 } 743 ) 744 ) 745 ); 746 unlock_decl->set_statements( unlock_statement ); 747 748 // pushes routines to declsToAddAfter to add at a later time 749 declsToAddAfter.push_back( lock_decl ); 750 declsToAddAfter.push_back( unlock_decl ); 636 751 } 637 752 … … 937 1052 assert( !thread_guard_decl ); 938 1053 thread_guard_decl = decl; 1054 } 1055 else if ( decl->name == "__mutex_stmt_lock_guard" && decl->body ) { 1056 assert( !lock_guard_decl ); 1057 lock_guard_decl = decl; 939 1058 } 940 1059 } … … 1081 1200 new PointerType( 1082 1201 noQualifiers, 1083 new StructInstType( 1084 noQualifiers, 1085 monitor_decl 1202 //new TypeofType( noQualifiers, args.front()->clone() ) 1203 new TypeofType( noQualifiers, new UntypedExpr( 1204 new NameExpr( "__get_type" ), 1205 { args.front()->clone() } 1206 ) 1086 1207 ) 1087 1208 ), … … 1093 1214 map_range < std::list<Initializer*> > ( args, [](Expression * var ){ 1094 1215 return new SingleInit( new UntypedExpr( 1095 new NameExpr( "get_monitor" ),1096 { var }1216 new NameExpr( "__get_ptr" ), 1217 { var } 1097 1218 ) ); 1219 //return new SingleInit( new AddressExpr( var ) ); 1098 1220 }) 1099 1221 ) 1100 1222 ); 1223 1224 StructInstType * lock_guard_struct = new StructInstType( noQualifiers, lock_guard_decl ); 1225 TypeExpr * lock_type_expr = new TypeExpr( 1226 new TypeofType( noQualifiers, new UntypedExpr( 1227 new NameExpr( "__get_type" ), 1228 { args.front()->clone() } 1229 ) 1230 ) 1231 ); 1232 1233 lock_guard_struct->parameters.push_back( lock_type_expr ) ; 1101 1234 1102 1235 // in reverse order : … … 1108 1241 LinkageSpec::Cforall, 1109 1242 nullptr, 1110 new StructInstType( 1111 noQualifiers, 1112 guard_decl 1113 ), 1243 lock_guard_struct, 1114 1244 new ListInit( 1115 1245 { -
src/ControlStruct/ExceptTranslate.cc
rf95634e rb7fd9daf 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // Except Visitor.cc --7 // ExceptTranslate.cc -- Conversion of exception control flow structures. 8 8 // 9 9 // Author : Andrew Beach -
src/ControlStruct/ExceptTranslate.h
rf95634e rb7fd9daf 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // ExceptTranslate.h -- 7 // ExceptTranslate.h -- Conversion of exception control flow structures. 8 8 // 9 9 // Author : Andrew Beach 10 10 // Created On : Tus Jun 06 10:13:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Tus May 19 11:47:00 202013 // Update Count : 512 // Last Modified On : Mon Nov 8 11:43:00 2020 13 // Update Count : 6 14 14 // 15 15 … … 19 19 20 20 class Declaration; 21 namespace ast { 22 class TranslationUnit; 23 } 21 24 22 25 namespace ControlStruct { 23 26 void translateThrows( std::list< Declaration *> & translationUnit ); 27 void translateThrows( ast::TranslationUnit & transUnit ); 24 28 /* Replaces all throw & throwResume statements with function calls. 25 29 * These still need to be resolved, so call this before the reslover. -
src/ControlStruct/LabelGenerator.cc
rf95634e rb7fd9daf 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Mar 11 22:23:20 201913 // Update Count : 1 511 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 8 10:18:00 2021 13 // Update Count : 17 14 14 // 15 15 … … 19 19 20 20 #include "LabelGenerator.h" 21 22 #include "AST/Attribute.hpp" 23 #include "AST/Label.hpp" 24 #include "AST/Stmt.hpp" 21 25 #include "SynTree/Attribute.h" // for Attribute 22 26 #include "SynTree/Label.h" // for Label, operator<< … … 24 28 25 29 namespace ControlStruct { 26 LabelGenerator * LabelGenerator::labelGenerator = 0; 30 31 int LabelGenerator::current = 0; 32 LabelGenerator * LabelGenerator::labelGenerator = nullptr; 27 33 28 34 LabelGenerator * LabelGenerator::getGenerator() { … … 43 49 return l; 44 50 } 51 52 ast::Label LabelGenerator::newLabel( 53 const std::string & suffix, const ast::Stmt * stmt ) { 54 assert( stmt ); 55 56 std::ostringstream os; 57 os << "__L" << current++ << "__" << suffix; 58 if ( stmt && !stmt->labels.empty() ) { 59 os << "_" << stmt->labels.front() << "__"; 60 } 61 ast::Label ret_label( stmt->location, os.str() ); 62 ret_label.attributes.push_back( new ast::Attribute( "unused" ) ); 63 return ret_label; 64 } 65 45 66 } // namespace ControlStruct 46 67 -
src/ControlStruct/LabelGenerator.h
rf95634e rb7fd9daf 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sat Jul 22 09:20:14 201713 // Update Count : 611 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 8 10:16:00 2021 13 // Update Count : 8 14 14 // 15 15 … … 21 21 22 22 class Statement; 23 namespace ast { 24 class Stmt; 25 class Label; 26 } 23 27 24 28 namespace ControlStruct { 25 class LabelGenerator { 26 public: 27 static LabelGenerator *getGenerator(); 28 Label newLabel(std::string suffix, Statement * stmt = nullptr); 29 void reset() { current = 0; } 30 void rewind() { current--; } 31 protected: 32 LabelGenerator(): current(0) {} 33 private: 34 int current; 35 static LabelGenerator *labelGenerator; 36 }; 29 30 class LabelGenerator { 31 static int current; 32 static LabelGenerator *labelGenerator; 33 protected: 34 LabelGenerator() {} 35 public: 36 static LabelGenerator *getGenerator(); 37 static Label newLabel(std::string suffix, Statement * stmt = nullptr); 38 static ast::Label newLabel( const std::string&, const ast::Stmt * ); 39 static void reset() { current = 0; } 40 static void rewind() { current--; } 41 }; 42 37 43 } // namespace ControlStruct 38 44 -
src/ControlStruct/module.mk
rf95634e rb7fd9daf 18 18 ControlStruct/ExceptDecl.cc \ 19 19 ControlStruct/ExceptDecl.h \ 20 ControlStruct/FixLabels.cpp \ 21 ControlStruct/FixLabels.hpp \ 20 22 ControlStruct/ForExprMutator.cc \ 21 23 ControlStruct/ForExprMutator.h \ … … 26 28 ControlStruct/MLEMutator.cc \ 27 29 ControlStruct/MLEMutator.h \ 30 ControlStruct/MultiLevelExit.cpp \ 31 ControlStruct/MultiLevelExit.hpp \ 28 32 ControlStruct/Mutate.cc \ 29 33 ControlStruct/Mutate.h 30 34 31 SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc ControlStruct/ExceptTranslate.h 35 SRC += $(SRC_CONTROLSTRUCT) \ 36 ControlStruct/ExceptTranslateNew.cpp \ 37 ControlStruct/ExceptTranslate.cc \ 38 ControlStruct/ExceptTranslate.h 39 32 40 SRCDEMANGLE += $(SRC_CONTROLSTRUCT) 33 41 -
src/InitTweak/GenInit.cc
rf95634e rb7fd9daf 9 9 // Author : Rob Schluntz 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Fri Dec 13 23:15:10 201913 // Update Count : 18 411 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Oct 25 13:53:00 2021 13 // Update Count : 186 14 14 // 15 15 #include "GenInit.h" … … 24 24 #include "AST/Decl.hpp" 25 25 #include "AST/Init.hpp" 26 #include "AST/Pass.hpp" 26 27 #include "AST/Node.hpp" 27 28 #include "AST/Stmt.hpp" … … 294 295 } 295 296 297 namespace { 298 299 # warning Remove the _New suffix after the conversion is complete. 300 struct HoistArrayDimension_NoResolve_New final : 301 public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting, 302 public ast::WithGuards, public ast::WithConstTranslationUnit, 303 public ast::WithVisitorRef<HoistArrayDimension_NoResolve_New> { 304 void previsit( const ast::ObjectDecl * decl ); 305 const ast::DeclWithType * postvisit( const ast::ObjectDecl * decl ); 306 // Do not look for objects inside there declarations (and type). 307 void previsit( const ast::AggregateDecl * ) { visit_children = false; } 308 void previsit( const ast::NamedTypeDecl * ) { visit_children = false; } 309 void previsit( const ast::FunctionType * ) { visit_children = false; } 310 311 const ast::Type * hoist( const ast::Type * type ); 312 313 ast::Storage::Classes storageClasses; 314 }; 315 316 void HoistArrayDimension_NoResolve_New::previsit( 317 const ast::ObjectDecl * decl ) { 318 GuardValue( storageClasses ) = decl->storage; 319 } 320 321 const ast::DeclWithType * HoistArrayDimension_NoResolve_New::postvisit( 322 const ast::ObjectDecl * objectDecl ) { 323 return mutate_field( objectDecl, &ast::ObjectDecl::type, 324 hoist( objectDecl->type ) ); 325 } 326 327 const ast::Type * HoistArrayDimension_NoResolve_New::hoist( 328 const ast::Type * type ) { 329 static UniqueName dimensionName( "_array_dim" ); 330 331 if ( !isInFunction() || storageClasses.is_static ) { 332 return type; 333 } 334 335 if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) { 336 if ( nullptr == arrayType->dimension ) { 337 return type; 338 } 339 340 if ( !Tuples::maybeImpure( arrayType->dimension ) ) { 341 return type; 342 } 343 344 ast::ptr<ast::Type> dimType = transUnit().global.sizeType; 345 assert( dimType ); 346 add_qualifiers( dimType, ast::CV::Qualifiers( ast::CV::Const ) ); 347 348 ast::ObjectDecl * arrayDimension = new ast::ObjectDecl( 349 arrayType->dimension->location, 350 dimensionName.newName(), 351 dimType, 352 new ast::SingleInit( 353 arrayType->dimension->location, 354 arrayType->dimension 355 ) 356 ); 357 358 ast::ArrayType * mutType = ast::mutate( arrayType ); 359 mutType->dimension = new ast::VariableExpr( 360 arrayDimension->location, arrayDimension ); 361 declsToAddBefore.push_back( arrayDimension ); 362 363 mutType->base = hoist( mutType->base ); 364 return mutType; 365 } 366 return type; 367 } 368 369 struct ReturnFixer_New final : 370 public ast::WithStmtsToAdd<>, ast::WithGuards { 371 void previsit( const ast::FunctionDecl * decl ); 372 const ast::ReturnStmt * previsit( const ast::ReturnStmt * stmt ); 373 private: 374 const ast::FunctionDecl * funcDecl = nullptr; 375 }; 376 377 void ReturnFixer_New::previsit( const ast::FunctionDecl * decl ) { 378 GuardValue( funcDecl ) = decl; 379 } 380 381 const ast::ReturnStmt * ReturnFixer_New::previsit( 382 const ast::ReturnStmt * stmt ) { 383 auto & returns = funcDecl->returns; 384 assert( returns.size() < 2 ); 385 // Hands off if the function returns a reference. 386 // Don't allocate a temporary if the address is returned. 387 if ( stmt->expr && 1 == returns.size() ) { 388 ast::ptr<ast::DeclWithType> retDecl = returns.front(); 389 if ( isConstructable( retDecl->get_type() ) ) { 390 // Explicitly construct the return value using the return 391 // expression and the retVal object. 392 assertf( "" != retDecl->name, 393 "Function %s has unnamed return value.\n", 394 funcDecl->name.c_str() ); 395 396 auto retVal = retDecl.strict_as<ast::ObjectDecl>(); 397 if ( auto varExpr = stmt->expr.as<ast::VariableExpr>() ) { 398 // Check if the return statement is already set up. 399 if ( varExpr->var == retVal ) return stmt; 400 } 401 ast::ptr<ast::Stmt> ctorStmt = genCtorDtor( 402 retVal->location, "?{}", retVal, stmt->expr ); 403 assertf( ctorStmt, 404 "ReturnFixer: genCtorDtor returned nllptr: %s / %s", 405 toString( retVal ).c_str(), 406 toString( stmt->expr ).c_str() ); 407 stmtsToAddBefore.push_back( ctorStmt ); 408 409 // Return the retVal object. 410 ast::ReturnStmt * mutStmt = ast::mutate( stmt ); 411 mutStmt->expr = new ast::VariableExpr( 412 stmt->location, retDecl ); 413 return mutStmt; 414 } 415 } 416 return stmt; 417 } 418 419 } // namespace 420 421 void genInit( ast::TranslationUnit & transUnit ) { 422 ast::Pass<HoistArrayDimension_NoResolve_New>::run( transUnit ); 423 ast::Pass<ReturnFixer_New>::run( transUnit ); 424 } 425 296 426 void CtorDtor::generateCtorDtor( std::list< Declaration * > & translationUnit ) { 297 427 PassVisitor<CtorDtor> ctordtor; -
src/InitTweak/GenInit.h
rf95634e rb7fd9daf 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sat Jul 22 09:31:19 201713 // Update Count : 411 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Oct 22 16:08:00 2021 13 // Update Count : 6 14 14 // 15 15 … … 27 27 /// Adds return value temporaries and wraps Initializers in ConstructorInit nodes 28 28 void genInit( std::list< Declaration * > & translationUnit ); 29 void genInit( ast::TranslationUnit & translationUnit ); 29 30 30 31 /// Converts return statements into copy constructor calls on the hidden return variable -
src/MakeLibCfa.h
rf95634e rb7fd9daf 19 19 20 20 class Declaration; 21 namespace ast { 22 struct TranslationUnit; 23 } 21 24 22 25 namespace LibCfa { 23 26 void makeLibCfa( std::list< Declaration* > &prelude ); 27 void makeLibCfa( ast::TranslationUnit & translationUnit ); 24 28 } // namespace LibCfa 25 29 -
src/Makefile.am
rf95634e rb7fd9daf 23 23 CompilationState.h \ 24 24 MakeLibCfa.cc \ 25 MakeLibCfaNew.cpp \ 25 26 MakeLibCfa.h 26 27 -
src/Parser/parser.yy
rf95634e rb7fd9daf 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Jul 20 22:03:04202113 // Update Count : 5 03112 // Last Modified On : Fri Oct 15 09:20:17 2021 13 // Update Count : 5163 14 14 // 15 15 … … 31 31 // from ANSI90 to ANSI11 C are marked with the comment "C99/C11". 32 32 33 // This grammar also has two levels of extensions. The first extensions cover most of the GCC C extensions All of the33 // This grammar also has two levels of extensions. The first extensions cover most of the GCC C extensions. All of the 34 34 // syntactic extensions for GCC C are marked with the comment "GCC". The second extensions are for Cforall (CFA), which 35 35 // fixes several of C's outstanding problems and extends C with many modern language concepts. All of the syntactic … … 69 69 // 2. String encodings are transformed into canonical form (one encoding at start) so the encoding can be found 70 70 // without searching the string, e.g.: "abc" L"def" L"ghi" => L"abc" "def" "ghi". Multiple encodings must match, 71 // i.e., u"a" U"b" L"c" is disallowed.71 // e.g., u"a" U"b" L"c" is disallowed. 72 72 73 73 if ( from[0] != '"' ) { // encoding ? … … 185 185 type = new ExpressionNode( new CastExpr( maybeMoveBuild<Expression>(type), new BasicType( Type::Qualifiers(), BasicType::SignedInt ) ) ); 186 186 } // if 187 // type = new ExpressionNode( build_func( new ExpressionNode( build_varref( new string( "__for_control_index_constraints__" ) ) ), type ) ); 187 188 return new ForCtrl( 188 189 distAttr( DeclarationNode::newTypeof( type, true ), DeclarationNode::newName( index )->addInitializer( new InitializerNode( start ) ) ), … … 309 310 %token ATassign // @= 310 311 311 %type<tok> identifier 312 %type<tok> identifier_or_type_name attr_name 312 %type<tok> identifier identifier_at identifier_or_type_name attr_name 313 313 %type<tok> quasi_keyword 314 314 %type<constant> string_literal … … 326 326 %type<en> conditional_expression constant_expression assignment_expression assignment_expression_opt 327 327 %type<en> comma_expression comma_expression_opt 328 %type<en> argument_expression_list_opt argument_expression default_initializer_opt328 %type<en> argument_expression_list_opt argument_expression_list argument_expression default_initializer_opt 329 329 %type<ifctl> if_control_expression 330 330 %type<fctl> for_control_expression for_control_expression_list … … 558 558 IDENTIFIER 559 559 | quasi_keyword 560 ; 561 562 identifier_at: 563 identifier 560 564 | '@' // CFA 561 565 { Token tok = { new string( DeclarationNode::anonymous.newName() ), yylval.tok.loc }; $$ = tok; } … … 692 696 // empty 693 697 { $$ = nullptr; } 694 | argument_expression 698 | argument_expression_list 699 ; 700 701 argument_expression_list: 702 argument_expression 695 703 | argument_expression_list_opt ',' argument_expression 696 704 { $$ = (ExpressionNode *)($1->set_last( $3 )); } … … 730 738 | FLOATINGconstant fraction_constants_opt 731 739 { $$ = new ExpressionNode( build_field_name_fraction_constants( build_field_name_FLOATINGconstant( *$1 ), $2 ) ); } 732 | identifier fraction_constants_opt740 | identifier_at fraction_constants_opt // CFA, allow anonymous fields 733 741 { 734 742 $$ = new ExpressionNode( build_field_name_fraction_constants( build_varref( $1 ), $2 ) ); … … 1083 1091 comma_expression_opt ';' 1084 1092 { $$ = new StatementNode( build_expr( $1 ) ); } 1093 | MUTEX '(' ')' comma_expression ';' 1094 { $$ = new StatementNode( build_mutex( nullptr, new StatementNode( build_expr( $4 ) ) ) ); } 1095 // { SemanticError( yylloc, "Mutex expression is currently unimplemented." ); $$ = nullptr; } 1085 1096 ; 1086 1097 … … 1181 1192 1182 1193 iteration_statement: 1183 WHILE '(' push if_control_expression ')' statement pop 1184 { $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 ) ) ); } 1185 | WHILE '(' ')' statement // CFA => while ( 1 ) 1194 WHILE '(' ')' statement // CFA => while ( 1 ) 1186 1195 { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); } 1187 | DO statement WHILE '(' comma_expression ')' ';' 1188 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); } 1196 | WHILE '(' if_control_expression ')' statement %prec THEN 1197 { $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ) ) ); } 1198 | WHILE '(' if_control_expression ')' statement ELSE statement // CFA 1199 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1189 1200 | DO statement WHILE '(' ')' ';' // CFA => do while( 1 ) 1190 1201 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); } 1191 | FOR '(' push for_control_expression_list ')' statement pop 1192 { $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 ) ) ); } 1202 | DO statement WHILE '(' comma_expression ')' ';' %prec THEN 1203 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); } 1204 | DO statement WHILE '(' comma_expression ')' ELSE statement // CFA 1205 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1193 1206 | FOR '(' ')' statement // CFA => for ( ;; ) 1194 1207 { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); } 1208 | FOR '(' for_control_expression_list ')' statement %prec THEN 1209 { $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ) ) ); } 1210 | FOR '(' for_control_expression_list ')' statement ELSE statement // CFA 1211 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1195 1212 ; 1196 1213 … … 1338 1355 with_statement: 1339 1356 WITH '(' tuple_expression_list ')' statement 1340 { 1341 $$ = new StatementNode( build_with( $3, $5 ) ); 1342 } 1357 { $$ = new StatementNode( build_with( $3, $5 ) ); } 1343 1358 ; 1344 1359 1345 1360 // If MUTEX becomes a general qualifier, there are shift/reduce conflicts, so change syntax to "with mutex". 1346 1361 mutex_statement: 1347 MUTEX '(' argument_expression_list _opt')' statement1362 MUTEX '(' argument_expression_list ')' statement 1348 1363 { $$ = new StatementNode( build_mutex( $3, $5 ) ); } 1349 1364 ; … … 2445 2460 | simple_assignment_operator initializer { $$ = $1 == OperKinds::Assign ? $2 : $2->set_maybeConstructed( false ); } 2446 2461 | '=' VOID { $$ = new InitializerNode( true ); } 2462 | '{' initializer_list_opt comma_opt '}' { $$ = new InitializerNode( $2, true ); } 2447 2463 ; 2448 2464 … … 2458 2474 | designation initializer { $$ = $2->set_designators( $1 ); } 2459 2475 | initializer_list_opt ',' initializer { $$ = (InitializerNode *)( $1->set_last( $3 ) ); } 2460 | initializer_list_opt ',' designation initializer 2461 { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); } 2476 | initializer_list_opt ',' designation initializer { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); } 2462 2477 ; 2463 2478 … … 2474 2489 designation: 2475 2490 designator_list ':' // C99, CFA uses ":" instead of "=" 2476 | identifier ':' // GCC, field name2491 | identifier_at ':' // GCC, field name 2477 2492 { $$ = new ExpressionNode( build_varref( $1 ) ); } 2478 2493 ; … … 2486 2501 2487 2502 designator: 2488 '.' identifier // C99, field name2503 '.' identifier_at // C99, field name 2489 2504 { $$ = new ExpressionNode( build_varref( $2 ) ); } 2490 2505 | '[' push assignment_expression pop ']' // C99, single array element … … 2918 2933 2919 2934 paren_identifier: 2920 identifier 2935 identifier_at 2921 2936 { $$ = DeclarationNode::newName( $1 ); } 2922 2937 | '(' paren_identifier ')' // redundant parenthesis -
src/ResolvExpr/module.mk
rf95634e rb7fd9daf 61 61 ResolvExpr/WidenMode.h 62 62 63 SRC += $(SRC_RESOLVEXPR) \ 64 ResolvExpr/AlternativePrinter.cc \ 65 ResolvExpr/AlternativePrinter.h \ 66 ResolvExpr/CandidatePrinter.cpp \ 67 ResolvExpr/CandidatePrinter.hpp 63 68 64 SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc ResolvExpr/AlternativePrinter.h65 69 SRCDEMANGLE += $(SRC_RESOLVEXPR) -
src/Tuples/TupleExpansionNew.cpp
rf95634e rb7fd9daf 8 8 // 9 9 // Author : Henry Xue 10 // Created On : Wed Aug 18 12:54:02202110 // Created On : Mon Aug 23 15:36:09 2021 11 11 // Last Modified By : Henry Xue 12 // Last Modified On : Wed Aug 18 12:54:02202112 // Last Modified On : Mon Aug 23 15:36:09 2021 13 13 // Update Count : 1 14 14 // 15 16 // Currently not working due to unresolved issues with UniqueExpr17 15 18 16 #include "Tuples.h" … … 20 18 namespace Tuples { 21 19 namespace { 20 struct MemberTupleExpander final : public ast::WithShortCircuiting, public ast::WithVisitorRef< MemberTupleExpander > { 21 void previsit( const ast::UntypedMemberExpr * ) { visit_children = false; } 22 const ast::Expr * postvisit( const ast::UntypedMemberExpr * memberExpr ); 23 }; 22 24 struct UniqueExprExpander final : public ast::WithDeclsToAdd<> { 23 25 const ast::Expr * postvisit( const ast::UniqueExpr * unqExpr ); 24 26 std::map< int, ast::ptr<ast::Expr> > decls; // not vector, because order added may not be increasing order 25 27 }; 28 } // namespace 29 30 void expandMemberTuples( ast::TranslationUnit & translationUnit ) { 31 ast::Pass< MemberTupleExpander >::run( translationUnit ); 32 } 33 34 namespace { 35 namespace { 36 /// given a expression representing the member and an expression representing the aggregate, 37 /// reconstructs a flattened UntypedMemberExpr with the right precedence 38 const ast::Expr * reconstructMemberExpr( const ast::Expr * member, const ast::Expr * aggr, const CodeLocation & loc ) { 39 if ( auto memberExpr = dynamic_cast< const ast::UntypedMemberExpr * >( member ) ) { 40 // construct a new UntypedMemberExpr with the correct structure , and recursively 41 // expand that member expression. 42 ast::Pass< MemberTupleExpander > expander; 43 auto inner = new ast::UntypedMemberExpr( loc, memberExpr->aggregate, aggr ); 44 auto newMemberExpr = new ast::UntypedMemberExpr( loc, memberExpr->member, inner ); 45 //delete memberExpr; 46 return newMemberExpr->accept( expander ); 47 } else { 48 // not a member expression, so there is nothing to do but attach and return 49 return new ast::UntypedMemberExpr( loc, member, aggr ); 50 } 51 } 52 } 53 54 const ast::Expr * MemberTupleExpander::postvisit( const ast::UntypedMemberExpr * memberExpr ) { 55 const CodeLocation loc = memberExpr->location; 56 if ( auto tupleExpr = memberExpr->member.as< ast::UntypedTupleExpr >() ) { 57 auto mutExpr = mutate( tupleExpr ); 58 ast::ptr< ast::Expr > aggr = memberExpr->aggregate->accept( *visitor ); 59 // aggregate expressions which might be impure must be wrapped in unique expressions 60 if ( Tuples::maybeImpureIgnoreUnique( memberExpr->aggregate ) ) aggr = new ast::UniqueExpr( loc, aggr ); 61 for ( auto & expr : mutExpr->exprs ) { 62 expr = reconstructMemberExpr( expr, aggr, loc ); 63 } 64 //delete aggr; 65 return mutExpr; 66 } else { 67 // there may be a tuple expr buried in the aggregate 68 return new ast::UntypedMemberExpr( loc, memberExpr->member, memberExpr->aggregate->accept( *visitor ) ); 69 } 70 } 26 71 } // namespace 27 72 -
src/Tuples/Tuples.h
rf95634e rb7fd9daf 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Tue Jun 18 09:36:00 201913 // Update Count : 1 811 // Last Modified By : Henry Xue 12 // Last Modified On : Mon Aug 23 15:36:09 2021 13 // Update Count : 19 14 14 // 15 15 … … 39 39 /// expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate 40 40 void expandMemberTuples( std::list< Declaration * > & translationUnit ); 41 void expandMemberTuples( ast::TranslationUnit & translationUnit ); 41 42 42 43 /// replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc. -
src/Tuples/module.mk
rf95634e rb7fd9daf 10 10 ## Author : Richard C. Bilson 11 11 ## Created On : Mon Jun 1 17:49:17 2015 12 ## Last Modified By : Peter A. Buhr13 ## Last Modified On : Mon Jun 1 17:54:33 201514 ## Update Count : 112 ## Last Modified By : Henry Xue 13 ## Last Modified On : Mon Aug 23 15:36:09 2021 14 ## Update Count : 2 15 15 ############################################################################### 16 16 -
src/main.cc
rf95634e rb7fd9daf 9 9 // Author : Peter Buhr and Rob Schluntz 10 10 // Created On : Fri May 15 23:12:02 2015 11 // Last Modified By : Henry Xue12 // Last Modified On : Tue Jul 20 04:27:35202113 // Update Count : 65 811 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Nov 9 11:10:00 2021 13 // Update Count : 657 14 14 // 15 15 … … 43 43 #include "Common/CodeLocationTools.hpp" // for forceFillCodeLocations 44 44 #include "Common/CompilerError.h" // for CompilerError 45 #include "Common/DeclStats.hpp" // for printDeclStats 46 #include "Common/ResolvProtoDump.hpp" // for dumpAsResolverProto 45 47 #include "Common/Stats.h" 46 48 #include "Common/PassVisitor.h" … … 51 53 #include "ControlStruct/ExceptDecl.h" // for translateExcept 52 54 #include "ControlStruct/ExceptTranslate.h" // for translateEHM 55 #include "ControlStruct/FixLabels.hpp" // for fixLabels 53 56 #include "ControlStruct/Mutate.h" // for mutate 54 57 #include "GenPoly/Box.h" // for box … … 62 65 #include "Parser/TypedefTable.h" // for TypedefTable 63 66 #include "ResolvExpr/AlternativePrinter.h" // for AlternativePrinter 67 #include "ResolvExpr/CandidatePrinter.hpp" // for printCandidates 64 68 #include "ResolvExpr/Resolver.h" // for resolve 65 69 #include "SymTab/Validate.h" // for validate … … 315 319 // add the assignment statement after the initialization of a type parameter 316 320 PASS( "Validate", SymTab::validate( translationUnit, symtabp ) ); 317 if ( symtabp ) {318 deleteAll( translationUnit );319 return EXIT_SUCCESS;320 } // if321 322 if ( expraltp ) {323 PassVisitor<ResolvExpr::AlternativePrinter> printer( cout );324 acceptAll( translationUnit, printer );325 return EXIT_SUCCESS;326 } // if327 328 if ( validp ) {329 dump( translationUnit );330 return EXIT_SUCCESS;331 } // if332 333 PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) );334 PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) );335 PASS( "Fix Names", CodeGen::fixNames( translationUnit ) );336 PASS( "Gen Init", InitTweak::genInit( translationUnit ) );337 PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( translationUnit ) );338 if ( libcfap ) {339 // generate the bodies of cfa library functions340 LibCfa::makeLibCfa( translationUnit );341 } // if342 343 if ( declstatsp ) {344 CodeTools::printDeclStats( translationUnit );345 deleteAll( translationUnit );346 return EXIT_SUCCESS;347 } // if348 349 if ( bresolvep ) {350 dump( translationUnit );351 return EXIT_SUCCESS;352 } // if353 321 354 322 CodeTools::fillLocations( translationUnit ); 355 356 if ( resolvprotop ) {357 CodeTools::dumpAsResolvProto( translationUnit );358 return EXIT_SUCCESS;359 } // if360 323 361 324 if( useNewAST ) { … … 365 328 } 366 329 auto transUnit = convert( move( translationUnit ) ); 330 331 forceFillCodeLocations( transUnit ); 332 333 if ( symtabp ) { 334 return EXIT_SUCCESS; 335 } // if 336 337 if ( expraltp ) { 338 ResolvExpr::printCandidates( transUnit ); 339 return EXIT_SUCCESS; 340 } // if 341 342 if ( validp ) { 343 dump( move( transUnit ) ); 344 return EXIT_SUCCESS; 345 } // if 346 347 PASS( "Translate Throws", ControlStruct::translateThrows( transUnit ) ); 348 PASS( "Fix Labels", ControlStruct::fixLabels( transUnit ) ); 349 PASS( "Fix Names", CodeGen::fixNames( transUnit ) ); 350 PASS( "Gen Init", InitTweak::genInit( transUnit ) ); 351 PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( transUnit ) ); 352 353 if ( libcfap ) { 354 // Generate the bodies of cfa library functions. 355 LibCfa::makeLibCfa( transUnit ); 356 } // if 357 358 if ( declstatsp ) { 359 printDeclStats( transUnit ); 360 return EXIT_SUCCESS; 361 } // if 362 363 if ( bresolvep ) { 364 dump( move( transUnit ) ); 365 return EXIT_SUCCESS; 366 } // if 367 368 if ( resolvprotop ) { 369 dumpAsResolverProto( transUnit ); 370 return EXIT_SUCCESS; 371 } // if 372 367 373 PASS( "Resolve", ResolvExpr::resolve( transUnit ) ); 368 374 if ( exprp ) { … … 385 391 translationUnit = convert( move( transUnit ) ); 386 392 } else { 393 if ( symtabp ) { 394 deleteAll( translationUnit ); 395 return EXIT_SUCCESS; 396 } // if 397 398 if ( expraltp ) { 399 PassVisitor<ResolvExpr::AlternativePrinter> printer( cout ); 400 acceptAll( translationUnit, printer ); 401 return EXIT_SUCCESS; 402 } // if 403 404 if ( validp ) { 405 dump( translationUnit ); 406 return EXIT_SUCCESS; 407 } // if 408 409 PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) ); 410 PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) ); 411 PASS( "Fix Names", CodeGen::fixNames( translationUnit ) ); 412 PASS( "Gen Init", InitTweak::genInit( translationUnit ) ); 413 PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( translationUnit ) ); 414 415 if ( libcfap ) { 416 // Generate the bodies of cfa library functions. 417 LibCfa::makeLibCfa( translationUnit ); 418 } // if 419 420 if ( declstatsp ) { 421 CodeTools::printDeclStats( translationUnit ); 422 deleteAll( translationUnit ); 423 return EXIT_SUCCESS; 424 } // if 425 426 if ( bresolvep ) { 427 dump( translationUnit ); 428 return EXIT_SUCCESS; 429 } // if 430 431 CodeTools::fillLocations( translationUnit ); 432 433 if ( resolvprotop ) { 434 CodeTools::dumpAsResolvProto( translationUnit ); 435 return EXIT_SUCCESS; 436 } // if 437 387 438 PASS( "Resolve", ResolvExpr::resolve( translationUnit ) ); 388 439 if ( exprp ) { … … 447 498 PASS( "Code Gen", CodeGen::generate( translationUnit, *output, ! genproto, prettycodegenp, true, linemarks ) ); 448 499 449 CodeGen::FixMain::fix( *output, (PreludeDirector + "/bootloader.c").c_str() ); 500 CodeGen::FixMain::fix( translationUnit, *output, 501 (PreludeDirector + "/bootloader.c").c_str() ); 450 502 if ( output != &cout ) { 451 503 delete output; -
tests/.expect/declarationSpecifier.x64.txt
rf95634e rb7fd9daf 1132 1132 char **_X13cfa_args_argvPPc_1; 1133 1133 char **_X13cfa_args_envpPPc_1; 1134 signed int _X17cfa_main_returnedi_1 = ((signed int )0);1134 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1; 1135 1135 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 1136 1136 __attribute__ ((unused)) signed int _X12_retval_maini_1; … … 1149 1149 signed int _tmp_cp_ret6; 1150 1150 signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6); 1151 { 1152 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 1151 if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) { 1152 { 1153 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 1154 } 1155 1153 1156 } 1154 1157 -
tests/.expect/declarationSpecifier.x86.txt
rf95634e rb7fd9daf 1132 1132 char **_X13cfa_args_argvPPc_1; 1133 1133 char **_X13cfa_args_envpPPc_1; 1134 signed int _X17cfa_main_returnedi_1 = ((signed int )0);1134 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1; 1135 1135 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 1136 1136 __attribute__ ((unused)) signed int _X12_retval_maini_1; … … 1149 1149 signed int _tmp_cp_ret6; 1150 1150 signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6); 1151 { 1152 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 1151 if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) { 1152 { 1153 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 1154 } 1155 1153 1156 } 1154 1157 -
tests/.expect/gccExtensions.x64.txt
rf95634e rb7fd9daf 324 324 char **_X13cfa_args_argvPPc_1; 325 325 char **_X13cfa_args_envpPPc_1; 326 signed int _X17cfa_main_returnedi_1 = ((signed int )0);326 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1; 327 327 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 328 328 __attribute__ ((unused)) signed int _X12_retval_maini_1; … … 341 341 signed int _tmp_cp_ret6; 342 342 signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6); 343 { 344 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 343 if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) { 344 { 345 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 346 } 347 345 348 } 346 349 -
tests/.expect/gccExtensions.x86.txt
rf95634e rb7fd9daf 302 302 char **_X13cfa_args_argvPPc_1; 303 303 char **_X13cfa_args_envpPPc_1; 304 signed int _X17cfa_main_returnedi_1 = ((signed int )0);304 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1; 305 305 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 306 306 __attribute__ ((unused)) signed int _X12_retval_maini_1; … … 319 319 signed int _tmp_cp_ret6; 320 320 signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6); 321 { 322 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 321 if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) { 322 { 323 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 324 } 325 323 326 } 324 327 -
tests/Makefile.am
rf95634e rb7fd9daf 75 75 pybin/tools.py \ 76 76 long_tests.hfa \ 77 .in/parseconfig-all.txt \ 78 .in/parseconfig-errors.txt \ 79 .in/parseconfig-missing.txt \ 77 80 io/.in/io.data \ 78 81 io/.in/many_read.data \ -
tests/concurrent/mutexstmt/locks.cfa
rf95634e rb7fd9daf 1 #include <mutex_stmt _locks.hfa>1 #include <mutex_stmt.hfa> 2 2 #include <locks.hfa> 3 3 4 4 const unsigned int num_times = 10000; 5 5 6 owner_lock m1, m2, m3, m4, m5;6 single_acquisition_lock m1, m2, m3, m4, m5; 7 7 8 8 thread T_Mutex {}; … … 13 13 for (unsigned int i = 0; i < num_times; i++) { 14 14 mutex ( m1 ) count++; 15 mutex ( m1 ) { 15 mutex ( m1 ) { 16 16 assert(!insideFlag); 17 17 insideFlag = true; -
tests/concurrent/mutexstmt/monitors.cfa
rf95634e rb7fd9daf 1 1 #include <monitor.hfa> 2 #include <mutex_stmt.hfa> 2 3 #include <stdio.h> 3 4 #include <stdlib.hfa> … … 13 14 bool insideFlag = false; 14 15 int count = 0; 16 bool startFlag = false; 15 17 16 18 void main( T_Mutex & this ) { -
tests/concurrent/semaphore.cfa
rf95634e rb7fd9daf 2 2 #include <locks.hfa> 3 3 #include <thread.hfa> 4 #include <mutex_stmt.hfa> 4 5 5 6 enum { num_blockers = 17, num_unblockers = 13 }; … … 28 29 thrash(); 29 30 P(ben); 30 if(((thread&)this).seqable.next != 0p) sout | acquire |"Link not invalidated";31 if(((thread&)this).seqable.next != 0p) mutex(sout) sout | "Link not invalidated"; 31 32 thrash(); 32 33 } -
tests/concurrent/sleep.cfa
rf95634e rb7fd9daf 1 1 #include <fstream.hfa> 2 2 #include <thread.hfa> 3 #include <mutex_stmt.hfa> 3 4 #include <time.hfa> 4 5 … … 29 30 30 31 int main() { 31 sout | acquire| "start";32 mutex( sout ) sout | "start"; 32 33 { 33 34 slow_sleeper slow; … … 36 37 yield(); 37 38 } 38 sout | acquire| "done";39 mutex( sout ) sout | "done"; 39 40 } 40 41 -
tests/exceptions/.expect/type-check.txt
rf95634e rb7fd9daf 1 exceptions/type-check.cfa: 8:1 error: catch must have pointer to an exception type2 exceptions/type-check.cfa: 9:1 error: catch must have pointer to an exception type3 exceptions/type-check.cfa: 10:1 error: catchResume must have pointer to an exception type4 exceptions/type-check.cfa: 11:1 error: catchResume must have pointer to an exception type1 exceptions/type-check.cfa:6:1 error: catch must have pointer to an exception type 2 exceptions/type-check.cfa:7:1 error: catch must have pointer to an exception type 3 exceptions/type-check.cfa:8:1 error: catchResume must have pointer to an exception type 4 exceptions/type-check.cfa:9:1 error: catchResume must have pointer to an exception type -
tests/exceptions/cancel/coroutine.cfa
rf95634e rb7fd9daf 2 2 3 3 #include <coroutine.hfa> 4 #include <exception.hfa>5 4 6 EHM_EXCEPTION(internal_error)();7 EHM_VIRTUAL_TABLE(internal_error, internal_vt);5 exception internal_error {}; 6 vtable(internal_error) internal_vt; 8 7 9 8 coroutine WillCancel {}; -
tests/exceptions/cancel/thread.cfa
rf95634e rb7fd9daf 2 2 3 3 #include <thread.hfa> 4 #include <exception.hfa>5 4 6 EHM_EXCEPTION(internal_error)();7 EHM_VIRTUAL_TABLE(internal_error, internal_vt);5 exception internal_error {}; 6 vtable(internal_error) internal_vt; 8 7 9 8 thread WillCancel {}; -
tests/exceptions/conditional.cfa
rf95634e rb7fd9daf 4 4 // up the non-trivial exception is reasonable to do. 5 5 6 #include <exception.hfa> 6 exception num_error { 7 int num; 8 }; 7 9 8 EHM_EXCEPTION(num_error)( 9 int num; 10 ); 11 12 EHM_VIRTUAL_TABLE(num_error, num_error_vt); 10 vtable(num_error) num_error_vt; 13 11 14 12 void caught_num_error(int expect, num_error * actual) { -
tests/exceptions/data-except.cfa
rf95634e rb7fd9daf 1 1 // Test exceptions that add data but no functionality. 2 2 3 #include <exception.hfa> 4 5 EHM_EXCEPTION(paired)( 3 exception paired { 6 4 int first; 7 5 int second; 8 );6 }; 9 7 10 EHM_VIRTUAL_TABLE(paired, paired_vt);8 vtable(paired) paired_vt; 11 9 12 10 const char * virtual_msg(paired * this) { -
tests/exceptions/defaults.cfa
rf95634e rb7fd9daf 4 4 #include <exception.hfa> 5 5 6 EHM_EXCEPTION(log_message)( 6 exception log_message { 7 7 char * msg; 8 );8 }; 9 9 10 10 _EHM_DEFINE_COPY(log_message, ) … … 32 32 33 33 // I don't have a good use case for doing the same with termination. 34 EHM_EXCEPTION(jump)();34 exception jump {}; 35 35 void defaultTerminationHandler(jump &) { 36 36 printf("jump default handler.\n"); 37 37 } 38 38 39 EHM_VIRTUAL_TABLE(jump, jump_vt);39 vtable(jump) jump_vt; 40 40 41 41 void jump_test(void) { … … 48 48 } 49 49 50 EHM_EXCEPTION(first)();51 EHM_VIRTUAL_TABLE(first, first_vt);50 exception first {}; 51 vtable(first) first_vt; 52 52 53 EHM_EXCEPTION(unhandled_exception)();54 EHM_VIRTUAL_TABLE(unhandled_exception, unhandled_vt);53 exception unhandled_exception {}; 54 vtable(unhandled_exception) unhandled_vt; 55 55 56 56 void unhandled_test(void) { … … 69 69 } 70 70 71 EHM_EXCEPTION(second)();72 EHM_VIRTUAL_TABLE(second, second_vt);71 exception second {}; 72 vtable(second) second_vt; 73 73 74 74 void cross_test(void) { -
tests/exceptions/finally.cfa
rf95634e rb7fd9daf 1 1 // Finally Clause Tests 2 2 3 #include <exception.hfa>4 3 #include "except-io.hfa" 5 4 6 EHM_EXCEPTION(myth)();5 exception myth {}; 7 6 8 EHM_VIRTUAL_TABLE(myth, myth_vt);7 vtable(myth) myth_vt; 9 8 10 9 int main(int argc, char * argv[]) { -
tests/exceptions/interact.cfa
rf95634e rb7fd9daf 1 1 // Testing Interactions Between Termination and Resumption 2 2 3 #include <exception.hfa>4 3 #include "except-io.hfa" 5 4 6 EHM_EXCEPTION(star)();7 EHM_EXCEPTION(moon)();5 exception star {}; 6 exception moon {}; 8 7 9 EHM_VIRTUAL_TABLE(star, star_vt);10 EHM_VIRTUAL_TABLE(moon, moon_vt);8 vtable(star) star_vt; 9 vtable(moon) moon_vt; 11 10 12 11 int main(int argc, char * argv[]) { -
tests/exceptions/polymorphic.cfa
rf95634e rb7fd9daf 1 1 // Testing polymophic exception types. 2 2 3 #include <exception.hfa> 3 forall(T &) exception proxy {}; 4 4 5 EHM_FORALL_EXCEPTION(proxy, (T&), (T))(); 6 7 EHM_FORALL_VIRTUAL_TABLE(proxy, (int), proxy_int); 8 EHM_FORALL_VIRTUAL_TABLE(proxy, (char), proxy_char); 5 vtable(proxy(int)) proxy_int; 6 vtable(proxy(char)) proxy_char; 9 7 10 8 void proxy_test(void) { … … 33 31 } 34 32 35 EHM_FORALL_EXCEPTION(cell, (T), (T))( 33 forall(T) exception cell { 36 34 T data; 37 );35 }; 38 36 39 EHM_FORALL_VIRTUAL_TABLE(cell, (int), int_cell);40 EHM_FORALL_VIRTUAL_TABLE(cell, (char), char_cell);41 EHM_FORALL_VIRTUAL_TABLE(cell, (bool), bool_cell);37 vtable(cell(int)) int_cell; 38 vtable(cell(char)) char_cell; 39 vtable(cell(bool)) bool_cell; 42 40 43 41 void cell_test(void) { -
tests/exceptions/resume.cfa
rf95634e rb7fd9daf 1 1 // Resumption Exception Tests 2 2 3 #include <exception.hfa>4 3 #include "except-io.hfa" 5 4 6 EHM_EXCEPTION(yin)();7 EHM_EXCEPTION(yang)();8 EHM_EXCEPTION(zen)();5 exception yin {}; 6 exception yang {}; 7 exception zen {}; 9 8 10 EHM_VIRTUAL_TABLE(yin, yin_vt);11 EHM_VIRTUAL_TABLE(yang, yang_vt);12 EHM_VIRTUAL_TABLE(zen, zen_vt);9 vtable(yin) yin_vt; 10 vtable(yang) yang_vt; 11 vtable(zen) zen_vt; 13 12 14 13 void in_void(void); -
tests/exceptions/terminate.cfa
rf95634e rb7fd9daf 1 1 // Termination Exception Tests 2 2 3 #include <exception.hfa>4 3 #include "except-io.hfa" 5 4 6 EHM_EXCEPTION(yin)();7 EHM_EXCEPTION(yang)();8 EHM_EXCEPTION(zen)();5 exception yin {}; 6 exception yang {}; 7 exception zen {}; 9 8 10 EHM_VIRTUAL_TABLE(yin, yin_vt);11 EHM_VIRTUAL_TABLE(yang, yang_vt);12 EHM_VIRTUAL_TABLE(zen, zen_vt);9 vtable(yin) yin_vt; 10 vtable(yang) yang_vt; 11 vtable(zen) zen_vt; 13 12 14 13 void in_void(void); -
tests/exceptions/trash.cfa
rf95634e rb7fd9daf 1 1 // Make sure throw-catch during unwind does not trash internal data. 2 2 3 #include <exception.hfa> 3 exception yin {}; 4 exception yang {}; 4 5 5 EHM_EXCEPTION(yin)(); 6 EHM_EXCEPTION(yang)(); 7 8 EHM_VIRTUAL_TABLE(yin, yin_vt); 9 EHM_VIRTUAL_TABLE(yang, yang_vt); 6 vtable(yin) yin_vt; 7 vtable(yang) yang_vt; 10 8 11 9 int main(int argc, char * argv[]) { -
tests/exceptions/type-check.cfa
rf95634e rb7fd9daf 1 1 // Check that the exception type check works. 2 2 3 #include <exception.hfa> 4 5 EHM_EXCEPTION(truth)(); 3 exception truth {}; 6 4 7 5 int main(int argc, char * argv[]) { -
tests/io/io-acquire.cfa
rf95634e rb7fd9daf 10 10 // Created On : Mon Mar 1 18:40:09 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Apr 27 11:49:34202113 // Update Count : 1812 // Last Modified On : Wed Oct 6 18:04:58 2021 13 // Update Count : 72 14 14 // 15 15 16 16 #include <fstream.hfa> 17 17 #include <thread.hfa> 18 #include <mutex_stmt.hfa> 18 19 19 20 thread T {}; … … 21 22 // output from parallel threads should not be scrambled 22 23 23 for ( 100 ) { // localprotection24 sout | acquire| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;24 for ( 100 ) { // expression protection 25 mutex(sout) sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; 25 26 } 26 { // global protection (RAII) 27 osacquire acq = { sout }; 27 mutex( sout ) { // statement protection 28 28 for ( 100 ) { 29 29 sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; … … 31 31 } 32 32 { // duplicate protection demonstrating recursive lock 33 osacquire acq = { sout }; 34 for ( 100 ) { 35 osacquire acq = { sout }; 36 sout | acquire | 1 | 2 | 3 | 4 | 5 | acquire | 6 | 7 | 8 | 9; 37 sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; 33 ofstream & h1( ofstream & os ) { // helper 34 mutex( os ) return os | 1 | 2 | 3 | 4; // unnecessary mutex 35 } 36 ofstream & h2( ofstream & os ) { // helper 37 mutex( os ) return os | 6 | 7 | 8 | 9; // unnecessary mutex 38 } 39 mutex( sout ) { // unnecessary mutex 40 for ( 100 ) { 41 mutex( sout ) { 42 sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; 43 sout | h1 | 5 | h2; // refactored code 44 } 45 } 38 46 } 39 47 } … … 42 50 43 51 int a, b, c, d, e, f, g, h, i; 44 for ( 100 ) { // localprotection45 sin | acquire| a | b | c | d | e | f | g | h | i;52 for ( 100 ) { // expression protection 53 mutex(sin) sin | a | b | c | d | e | f | g | h | i; 46 54 } 47 { // global protection (RAII) 48 isacquire acq = { sin }; 55 mutex( sin ) { // statement protection 49 56 for ( 100 ) { 50 57 sin | a | b | c | d | e | f | g | h | i; … … 52 59 } 53 60 { // duplicate protection demonstrating recursive lock 54 isacquire acq = { sin }; 55 for ( 100 ) { 56 isacquire acq = { sin }; 57 sin | acquire | a | b | c | d | e | acquire | f | g | h | i; 58 sin | a | b | c | d | e | f | g | h | i; 61 ifstream & h1( ifstream & is ) { // helper 62 mutex( is ) return is | a | b | c | d; // unnecessary mutex 63 } 64 ifstream & h2( ifstream & is ) { // helper 65 mutex( is ) return is | f | g | h | i; // unnecessary mutex 66 } 67 mutex( sin ) { // unnecessary mutex 68 for ( 5 ) { 69 mutex( sin ) { 70 sin | a | b | c | d | e | f | g | h | i; 71 sin | h1 | e | h2; // refactored code 72 } 73 } 59 74 } 60 75 } -
tests/linking/io-acquire.cfa
rf95634e rb7fd9daf 17 17 #include <fstream.hfa> 18 18 #include <stdlib.hfa> 19 #include <mutex_stmt.hfa> 19 20 20 21 int main() { 21 22 int i; 22 23 if(threading_enabled()) { 23 stdout | acquire| "YES";24 mutex( stdout ) stdout | "YES"; 24 25 stdin | i; 25 26 } else { 26 stdout | acquire| "NO";27 mutex( stdout ) stdout | "NO"; 27 28 stdin | i; 28 29 } -
tests/pybin/test_run.py
rf95634e rb7fd9daf 65 65 def toString( cls, retcode, duration ): 66 66 if settings.generating : 67 if retcode == TestResult.SUCCESS: text = "Done "68 elif retcode == TestResult.TIMEOUT: text = "TIMEOUT"69 else : text = "ERROR code %d" % retcode67 if retcode == TestResult.SUCCESS: key = 'pass'; text = "Done " 68 elif retcode == TestResult.TIMEOUT: key = 'time'; text = "TIMEOUT" 69 else : key = 'fail'; text = "ERROR code %d" % retcode 70 70 else : 71 if retcode == TestResult.SUCCESS: text = "PASSED "72 elif retcode == TestResult.TIMEOUT: text = "TIMEOUT"73 else : text = "FAILED with code %d" % retcode71 if retcode == TestResult.SUCCESS: key = 'pass'; text = "PASSED " 72 elif retcode == TestResult.TIMEOUT: key = 'time'; text = "TIMEOUT" 73 else : key = 'fail'; text = "FAILED with code %d" % retcode 74 74 75 75 text += " C%s - R%s" % (fmtDur(duration[0]), fmtDur(duration[1])) 76 return text76 return key, text -
tests/test.py
rf95634e rb7fd9daf 257 257 258 258 # update output based on current action 259 result_ txt = TestResult.toString( retcode, duration )259 result_key, result_txt = TestResult.toString( retcode, duration ) 260 260 261 261 #print result with error if needed … … 265 265 text = text + '\n' + error 266 266 267 return retcode == TestResult.SUCCESS, text267 return retcode == TestResult.SUCCESS, result_key, text 268 268 except KeyboardInterrupt: 269 return False, ""269 return False, 'keybrd', "" 270 270 # except Exception as ex: 271 271 # print("Unexpected error in worker thread running {}: {}".format(t.target(), ex), file=sys.stderr) … … 283 283 284 284 failed = False 285 rescnts = { 'pass': 0, 'fail': 0, 'time': 0, 'keybrd': 0 } 286 other = 0 285 287 286 288 # for each test to run … … 294 296 ) 295 297 296 for i, (succ, txt) in enumerate(timed(results, timeout = settings.timeout.total), 1) : 298 for i, (succ, code, txt) in enumerate(timed(results, timeout = settings.timeout.total), 1) : 299 if code in rescnts.keys(): 300 rescnts[code] += 1 301 else: 302 other += 1 303 297 304 if not succ : 298 305 failed = True … … 319 326 # clean the workspace 320 327 make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL) 328 329 print("{} passes, {} failures, {} timeouts, {} cancelled, {} other".format(rescnts['pass'], rescnts['fail'], rescnts['time'], rescnts['keybrd'], other)) 321 330 322 331 return failed … … 443 452 failed = run_tests(local_tests, options.jobs) 444 453 if failed: 445 result = 1446 454 if not settings.continue_: 447 455 break -
tools/perf/process_stat_array.py
rf95634e rb7fd9daf 1 1 #!/usr/bin/python3 2 2 3 import argparse, os, sys, re 3 import argparse, json, math, os, sys, re 4 from PIL import Image 5 import numpy as np 4 6 5 7 def dir_path(string): … … 11 13 parser = argparse.ArgumentParser() 12 14 parser.add_argument('--path', type=dir_path, default=".cfadata", help= 'paste path to biog.txt file') 15 parser.add_argument('--out', type=argparse.FileType('w'), default=sys.stdout) 13 16 14 17 try : … … 23 26 counters = {} 24 27 28 max_cpu = 0 29 min_cpu = 1000000 30 max_tsc = 0 31 min_tsc = 18446744073709551615 32 25 33 #open the files 26 34 for filename in filenames: … … 31 39 with open(os.path.join(root, filename), 'r') as file: 32 40 for line in file: 33 # data = [int(x.strip()) for x in line.split(',')] 34 data = [int(line.strip())] 35 data = [me, *data] 41 raw = [int(x.strip()) for x in line.split(',')] 42 43 ## from/to 44 high = (raw[1] >> 32) 45 low = (raw[1] & 0xffffffff) 46 data = [me, raw[0], high, low] 47 max_cpu = max(max_cpu, high, low) 48 min_cpu = min(min_cpu, high, low) 49 50 ## number 51 # high = (raw[1] >> 8) 52 # low = (raw[1] & 0xff) 53 # data = [me, raw[0], high, low] 54 # max_cpu = max(max_cpu, low) 55 # min_cpu = min(min_cpu, low) 56 57 58 max_tsc = max(max_tsc, raw[0]) 59 min_tsc = min(min_tsc, raw[0]) 36 60 merged.append(data) 37 61 38 except: 62 except Exception as e: 63 print(e) 39 64 pass 40 65 66 67 print({"max-cpu": max_cpu, "min-cpu": min_cpu, "max-tsc": max_tsc, "min-tsc": min_tsc}) 41 68 42 69 # Sort by timestamp (the second element) … … 47 74 merged.sort(key=takeSecond) 48 75 49 # for m in merged: 50 # print(m) 76 json.dump({"values":merged, "max-cpu": max_cpu, "min-cpu": min_cpu, "max-tsc": max_tsc, "min-tsc": min_tsc}, args.out) 51 77 52 single = [] 53 curr = 0 78 # vmin = merged[ 0][1] 79 # vmax = float(merged[-1][1] - vmin) / 2500000000.0 80 # # print(vmax) 54 81 55 # merge the data 56 # for (me, time, value) in merged: 57 for (me, value) in merged: 58 # check now much this changes 59 old = counters[me] 60 change = value - old 61 counters[me] = value 82 # bins = [] 83 # for _ in range(0, int(math.ceil(vmax * 10))): 84 # bins.append([0] * (32 * 32)) 62 85 63 # add change to the current 64 curr = curr + change 65 single.append( value ) 86 # # print(len(bins)) 87 # bins = np.array(bins) 66 88 67 pass 89 # rejected = 0 90 # highest = 0 68 91 69 print(single) 92 # for x in merged: 93 # b = int(float(x[1] - vmin) / 250000000.0) 94 # from_ = x[2] 95 # if from_ < 0 or from_ > 32: 96 # rejected += 1 97 # continue; 98 # to_ = x[3] 99 # if to_ < 0 or to_ > 32: 100 # rejected += 1 101 # continue; 102 # idx = (to_ * 32) + from_ 103 # bins[b][idx] = bins[b][idx] + 1 104 # highest = max(highest, bins[b][idx]) 105 106 # bins = np.array(map(lambda x: np.int8(x * 255.0 / float(highest)), bins)) 107 108 # print([highest, rejected]) 109 # print(bins.shape) 110 111 # im = Image.fromarray(bins) 112 # im.save('test.png') 113 114 # vmax = merged[-1][1] 115 116 # diff = float(vmax - vmin) / 2500000000.0 117 # print([vmin, vmax]) 118 # print([vmax - vmin, diff]) 119 120 # print(len(merged)) 121 122 # for b in bins: 123 # print(b) 124 125 # single = [] 126 # curr = 0 127 128 # # merge the data 129 # # for (me, time, value) in merged: 130 # for (me, value) in merged: 131 # # check now much this changes 132 # old = counters[me] 133 # change = value - old 134 # counters[me] = value 135 136 # # add change to the current 137 # curr = curr + change 138 # single.append( value ) 139 140 # pass 141 142 # print(single) 70 143 71 144 # single = sorted(single)[:len(single)-100]
Note:
See TracChangeset
for help on using the changeset viewer.