Changes in / [b7fd9daf:f95634e]
- Files:
-
- 15 added
- 86 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 (deleted)
-
benchmark/mutexStmt/cpp1.cc (deleted)
-
benchmark/mutexStmt/cpp2.cc (deleted)
-
benchmark/mutexStmt/cpp4.cc (deleted)
-
benchmark/mutexStmt/cpp8.cc (deleted)
-
benchmark/mutexStmt/cppLock.hpp (deleted)
-
benchmark/mutexStmt/lock1.cfa (deleted)
-
benchmark/mutexStmt/lock2.cfa (deleted)
-
benchmark/mutexStmt/lock4.cfa (deleted)
-
benchmark/mutexStmt/lock8.cfa (deleted)
-
benchmark/mutexStmt/monitor1.cfa (deleted)
-
benchmark/mutexStmt/monitor2.cfa (deleted)
-
benchmark/mutexStmt/monitor4.cfa (deleted)
-
benchmark/mutexStmt/no_stmt_lock1.cfa (deleted)
-
benchmark/mutexStmt/no_stmt_lock2.cfa (deleted)
-
benchmark/mutexStmt/no_stmt_lock4.cfa (deleted)
-
benchmark/mutexStmt/no_stmt_lock8.cfa (deleted)
-
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 (deleted)
-
benchmark/readyQ/transfer.rs (deleted)
-
benchmark/readyQ/yield.cfa (modified) (1 diff)
-
benchmark/readyQ/yield.cpp (modified) (1 diff)
-
benchmark/readyQ/yield.go (deleted)
-
benchmark/readyQ/yield.rs (deleted)
-
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 (added)
-
doc/theses/andrew_beach_MMath/code/CrossFinally.java (added)
-
doc/theses/andrew_beach_MMath/code/FixupEmpty.java (deleted)
-
doc/theses/andrew_beach_MMath/code/FixupOther.java (deleted)
-
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 (deleted)
-
doc/theses/andrew_beach_MMath/code/TryFinally.java (deleted)
-
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 (deleted)
-
doc/theses/andrew_beach_MMath/code/cond-fixup.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/cond_catch.py (added)
-
doc/theses/andrew_beach_MMath/code/cross-catch.cfa (added)
-
doc/theses/andrew_beach_MMath/code/cross-catch.cpp (added)
-
doc/theses/andrew_beach_MMath/code/cross-finally.cfa (added)
-
doc/theses/andrew_beach_MMath/code/cross-resume.cfa (added)
-
doc/theses/andrew_beach_MMath/code/cross_catch.py (added)
-
doc/theses/andrew_beach_MMath/code/cross_finally.py (added)
-
doc/theses/andrew_beach_MMath/code/fixup-empty-f.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/fixup-empty-r.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/fixup-empty.cpp (deleted)
-
doc/theses/andrew_beach_MMath/code/fixup-empty.py (deleted)
-
doc/theses/andrew_beach_MMath/code/fixup-other-f.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/fixup-other-r.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/fixup-other.cpp (deleted)
-
doc/theses/andrew_beach_MMath/code/fixup-other.py (deleted)
-
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 (deleted)
-
doc/theses/andrew_beach_MMath/code/throw-finally.cfa (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/code/throw-finally.py (deleted)
-
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 (deleted)
-
doc/theses/andrew_beach_MMath/code/throw-with.py (deleted)
-
doc/theses/andrew_beach_MMath/code/throw_empty.py (added)
-
doc/theses/andrew_beach_MMath/code/throw_finally.py (added)
-
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/try-catch.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/try-catch.cpp (deleted)
-
doc/theses/andrew_beach_MMath/code/try-catch.py (deleted)
-
doc/theses/andrew_beach_MMath/code/try-finally.cfa (deleted)
-
doc/theses/andrew_beach_MMath/code/try-finally.py (deleted)
-
doc/theses/andrew_beach_MMath/code/try-resume.cfa (deleted)
-
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 (deleted)
-
doc/theses/andrew_beach_MMath/resumption-marking.fig (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/termhandle.fig (deleted)
-
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 (deleted)
-
doc/theses/andrew_beach_MMath/vtable-layout.fig (modified) (2 diffs)
-
doc/theses/andrew_beach_MMath/vtable.fig (added)
-
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 (added)
-
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 (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/eval_macro.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/thesis.tex (modified) (6 diffs)
-
doc/user/user.tex (modified) (7 diffs)
-
example/cpu.cfa (deleted)
-
example/maybe-await.rs (deleted)
-
example/unnecessary-arc.rs (deleted)
-
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 (deleted)
-
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 (deleted)
-
libcfa/src/containers/string.hfa (deleted)
-
libcfa/src/containers/string_res.cfa (deleted)
-
libcfa/src/containers/string_res.hfa (deleted)
-
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 (deleted)
-
libcfa/src/parseconfig.hfa (deleted)
-
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 (deleted)
-
src/Common/DeclStats.hpp (deleted)
-
src/Common/ResolvProtoDump.cpp (deleted)
-
src/Common/ResolvProtoDump.hpp (deleted)
-
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 (deleted)
-
src/ControlStruct/FixLabels.cpp (deleted)
-
src/ControlStruct/FixLabels.hpp (deleted)
-
src/ControlStruct/LabelGenerator.cc (modified) (4 diffs)
-
src/ControlStruct/LabelGenerator.h (modified) (2 diffs)
-
src/ControlStruct/MultiLevelExit.cpp (deleted)
-
src/ControlStruct/MultiLevelExit.hpp (deleted)
-
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 (deleted)
-
src/Makefile.am (modified) (1 diff)
-
src/Parser/parser.yy (modified) (17 diffs)
-
src/ResolvExpr/CandidatePrinter.cpp (deleted)
-
src/ResolvExpr/CandidatePrinter.hpp (deleted)
-
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 (deleted)
-
tests/.in/parseconfig-all.txt (deleted)
-
tests/.in/parseconfig-errors.txt (deleted)
-
tests/.in/parseconfig-missing.txt (deleted)
-
tests/Makefile.am (modified) (1 diff)
-
tests/collections/.expect/string-api-coverage.txt (deleted)
-
tests/collections/.expect/string-gc.txt (deleted)
-
tests/collections/.expect/string-overwrite.txt (deleted)
-
tests/collections/string-api-coverage.cfa (deleted)
-
tests/collections/string-gc.cfa (deleted)
-
tests/collections/string-overwrite.cfa (deleted)
-
tests/concurrent/mutexstmt/.expect/locks.txt (deleted)
-
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 (deleted)
-
tests/pybin/test_run.py (modified) (1 diff)
-
tests/test.py (modified) (6 diffs)
-
tools/perf/png.py (deleted)
-
tools/perf/process_stat_array.py (modified) (5 diffs)
-
tools/perf/sample.py (deleted)
Legend:
- Unmodified
- Added
- Removed
-
benchmark/Cargo.toml.in
rb7fd9daf rf95634e 6 6 7 7 [[bin]] 8 name = " rdq-cycle-tokio"8 name = "cycle-tokio" 9 9 path = "@abs_srcdir@/readyQ/cycle.rs" 10 10 11 11 [[bin]] 12 name = " rdq-locality-tokio"12 name = "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"22 14 23 15 [features] -
benchmark/Makefile.am
rb7fd9daf rf95634e 21 21 include $(top_srcdir)/tools/build/cfa.make 22 22 23 AM_CFLAGS = -O 3-Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror23 AM_CFLAGS = -O2 -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 appropriately200 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 216 199 schedint.csv: 217 200 echo "building $@" … … 374 357 ## ========================================================================================================= 375 358 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.run393 394 mutexStmt-lock1$(EXEEXT):395 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock1.cfa396 397 mutexStmt-lock2$(EXEEXT):398 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock2.cfa399 400 mutexStmt-lock4$(EXEEXT):401 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock4.cfa402 403 mutexStmt-lock8$(EXEEXT):404 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock8.cfa405 406 mutexStmt-cpp1$(EXEEXT):407 $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp1.cc408 409 mutexStmt-cpp2$(EXEEXT):410 $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp2.cc411 412 mutexStmt-cpp4$(EXEEXT):413 $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp4.cc414 415 mutexStmt-cpp8$(EXEEXT):416 $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp8.cc417 418 mutexStmt-monitor1$(EXEEXT):419 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor1.cfa420 421 mutexStmt-monitor2$(EXEEXT):422 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor2.cfa423 424 mutexStmt-monitor4$(EXEEXT):425 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor4.cfa426 427 mutexStmt-no-stmt-lock1$(EXEEXT):428 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock1.cfa429 430 mutexStmt-no-stmt-lock2$(EXEEXT):431 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock2.cfa432 433 mutexStmt-no-stmt-lock4$(EXEEXT):434 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock4.cfa435 436 mutexStmt-no-stmt-lock8$(EXEEXT):437 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock8.cfa438 439 mutexStmt-java$(EXEEXT):440 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/mutexStmt/JavaThread.java441 echo "#!/bin/sh" > a.out442 echo "java JavaThread \"$$""@\"" >> a.out443 chmod a+x a.out444 445 ## =========================================================================================================446 447 359 schedint$(EXEEXT) : \ 448 360 schedint-cfa1.run \ … … 612 524 ## ========================================================================================================= 613 525 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 526 %-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs 527 cd $(builddir) && cargo build --release 528 cp $(builddir)/target/release/$(basename $@) $@ -
benchmark/bench.h
rb7fd9daf rf95634e 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 #endif40 23 41 24 #ifndef BENCH_N -
benchmark/bench.rs
rb7fd9daf rf95634e 1 1 use std::io::{self, Write}; 2 use std::option;3 2 use std::sync::atomic::{AtomicU64, AtomicBool, Ordering}; 4 3 use std::time::{Instant,Duration}; 5 use std::u128;6 4 7 5 use clap::{Arg, ArgMatches}; … … 29 27 30 28 impl BenchData { 31 pub fn new(options: ArgMatches, nthreads: usize , default_it: option::Option<u64>) -> BenchData {29 pub fn new(options: ArgMatches, nthreads: usize) -> BenchData { 32 30 let (clock_mode, stop_count, duration) = if options.is_present("iterations") { 33 31 (false, 34 32 options.value_of("iterations").unwrap().parse::<u64>().unwrap(), 35 -1.0)36 } else if !default_it.is_none() {37 (false,38 default_it.unwrap(),39 33 -1.0) 40 34 } else { … … 54 48 } 55 49 56 #[allow(dead_code)]57 50 pub async fn wait(&self, start: &Instant) -> Duration{ 58 51 loop { … … 76 69 } 77 70 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
rb7fd9daf rf95634e 185 185 sout | "Filled cache from path \"" | path | "\" with" | fcount | "files"; 186 186 if( conflicts > 0 ) { 187 sout | "Found" | conflicts | "conflicts (s ize: " | file_cache.size | ", seed: " | options.file_cache.hash_seed | ")";187 sout | "Found" | conflicts | "conflicts (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
rb7fd9daf rf95634e 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 pForall\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 pForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",122 "HTTP/1.1 404 Not Found\r\nServer: Htt pForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",123 "HTTP/1.1 405 Method Not \r\nServer: Htt pForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",124 "HTTP/1.1 408 Request Timeout\r\nServer: Htt pForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",125 "HTTP/1.1 413 Payload Too Large\r\nServer: Htt pForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",126 "HTTP/1.1 414 URI Too Long\r\nServer: Htt pForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",120 "HTTP/1.1 200 OK\r\nServer: HttoForall\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: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 122 "HTTP/1.1 404 Not Found\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 123 "HTTP/1.1 405 Method Not \r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 124 "HTTP/1.1 408 Request Timeout\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 125 "HTTP/1.1 413 Payload Too Large\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n", 126 "HTTP/1.1 414 URI Too Long\r\nServer: HttoForall\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
rb7fd9daf rf95634e 190 190 init_protocol(); 191 191 { 192 Worker * workers = anew(options.clopts.nworkers);192 Worker workers[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();215 214 { 216 215 char buffer[128]; … … 250 249 251 250 sout | "Stopping connection threads..." | nonl; flush( sout ); 252 adelete(workers);253 251 } 254 252 sout | "done"; -
benchmark/io/http/options.cfa
rb7fd9daf rf95634e 21 21 false, // log 22 22 false, // stats 23 true, // interactive24 0, // redirect25 0, // redirect26 23 27 24 { // file_cache … … 55 52 // bool sqkpoll = false; 56 53 // bool iokpoll = false; 57 unsigned nentries = 0;54 unsigned nentries = 16; 58 55 bool isolate = false; 59 56 … … 65 62 {'\0', "isolate", "Create one cluster per processor", isolate, parse_settrue}, 66 63 {'\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},69 64 {'\0', "stats", "Enable statistics", options.stats, parse_settrue}, 70 {'\0', "shell", "Disable interactive mode", options.interactive, parse_setfalse},71 65 {'\0', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog}, 72 66 {'\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}, … … 85 79 parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left ); 86 80 87 if( nentries != 0 &&!is_pow2(nentries) ) {81 if( !is_pow2(nentries) ) { 88 82 unsigned v = nentries; 89 83 v--; … … 137 131 138 132 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 }162 133 } -
benchmark/io/http/options.hfa
rb7fd9daf rf95634e 10 10 bool log; 11 11 bool stats; 12 bool interactive;13 const char * reopen_stdout;14 const char * reopen_stderr;15 12 16 13 struct { -
benchmark/io/http/protocol.cfa
rb7fd9daf rf95634e 11 11 #include <fstream.hfa> 12 12 #include <iofwd.hfa> 13 #include <io/types.hfa>14 #include <mutex_stmt.hfa>15 13 16 14 #include <assert.h> … … 28 26 #define PLAINTEXT_MEMCPY 29 27 #define PLAINTEXT_NOCOPY 30 #define LINKED_IO31 28 32 29 struct https_msg_str { … … 56 53 } 57 54 58 static inline int answer( int fd, const char * it, int len ) {55 static inline int answer( int fd, const char * it, int len) { 59 56 while(len > 0) { 60 57 // Call write 61 58 int ret = cfa_send(fd, it, len, 0, CFA_IO_LAZY); 62 59 if( ret < 0 ) { 63 if( errno == ECONNRESET || errno == EPIPE ) { close(fd); return -ECONNRESET; }60 if( errno == ECONNRESET || errno == EPIPE ) return -ECONNRESET; 64 61 if( errno == EAGAIN || errno == EWOULDBLOCK) return -EAGAIN; 65 62 … … 80 77 } 81 78 82 static int fill_header(char * it, size_t size) { 79 int answer_header( int fd, size_t size ) { 80 char buffer[512]; 81 char * it = buffer; 83 82 memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len); 84 83 it += http_msgs[OK200]->len; 85 84 int len = http_msgs[OK200]->len; 86 85 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);93 86 return answer( fd, buffer, len ); 94 87 } … … 142 135 } 143 136 144 static int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) { 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 ) { 145 179 unsigned sflags = SPLICE_F_MOVE; // | SPLICE_F_MORE; 146 180 off_t offset = 0; … … 173 207 } 174 208 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 header237 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 read298 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 file305 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 read361 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 Write431 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 everything456 asm volatile("": : :"memory");457 cfa_io_submit( ctx, idxs, have, false );458 459 // wait for the results460 // Always wait for splice-in to complete as461 // we may need to kill the connection if it fails462 // If it already completed, this is a no-op463 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 2471 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 if479 // - both completed480 // - the header failed481 // -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 #else492 int ret = answer_header(fd, fsize);493 if( ret < 0 ) { close(fd); return ret; }494 return sendfile(pipe, fd, ans_fd, fsize);495 #endif496 }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 538 209 //============================================================================================= 539 210 … … 543 214 544 215 const char * original_http_msgs[] = { 545 "HTTP/1.1 200 OK\nServer: Htt pForall\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: Htt pForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",548 "HTTP/1.1 404 Not Found\nServer: Htt pForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",549 "HTTP/1.1 405 Method Not Allowed\nServer: Htt pForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",550 "HTTP/1.1 408 Request Timeout\nServer: Htt pForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",551 "HTTP/1.1 413 Payload Too Large\nServer: Htt pForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",552 "HTTP/1.1 414 URI Too Long\nServer: Htt pForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",216 "HTTP/1.1 200 OK\nServer: HttoForall\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: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 219 "HTTP/1.1 404 Not Found\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 220 "HTTP/1.1 405 Method Not Allowed\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 221 "HTTP/1.1 408 Request Timeout\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 222 "HTTP/1.1 413 Payload Too Large\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 223 "HTTP/1.1 414 URI Too Long\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n", 553 224 }; 554 225 … … 580 251 Time now = timeHiRes(); 581 252 strftime( buff, 100, "%a, %d %b %Y %H:%M:%S %Z", now ); 582 // if( options.log )sout | "Updated date to '" | buff | "'";253 sout | "Updated date to '" | buff | "'"; 583 254 584 255 for(i; KNOWN_CODES) { … … 593 264 this.idx = (this.idx + 1) % 2; 594 265 595 // if( options.log )sout | "Date thread sleeping";266 sout | "Date thread sleeping"; 596 267 597 268 sleep(1`s); -
benchmark/io/http/protocol.hfa
rb7fd9daf rf95634e 16 16 17 17 int answer_error( int fd, HttpCode code ); 18 int answer_header( int fd, size_t size ); 18 19 int answer_plaintext( int fd ); 19 20 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
rb7fd9daf rf95634e 122 122 } 123 123 124 // Send the header 125 int ret = answer_header(fd, count); 126 if( ret == -ECONNRESET ) break REQUEST; 127 124 128 // Send the desired file 125 int ret = answer_sendfile( this.pipe, fd, ans_fd, count);129 ret = sendfile( this.pipe, fd, ans_fd, count); 126 130 if( ret == -ECONNRESET ) break REQUEST; 127 131 … … 130 134 131 135 if( options.log ) sout | "=== Connection closed ==="; 136 close(fd); 132 137 continue CONNECTION; 133 138 } -
benchmark/readyQ/cycle.cpp
rb7fd9daf rf95634e 41 41 Fibre * threads[tthreads]; 42 42 Partner thddata[tthreads]; 43 for( unsignedi = 0; i < tthreads; i++) {43 for(int i = 0; i < tthreads; i++) { 44 44 unsigned pi = (i + nthreads) % tthreads; 45 45 thddata[i].next = &thddata[pi].self; 46 46 } 47 for( unsignedi = 0; i < tthreads; i++) {47 for(int 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( unsignedi = 0; i < nthreads; i++) {55 for(int i = 0; i < nthreads; i++) { 56 56 thddata[i].self.post(); 57 57 } … … 62 62 printf("\nDone\n"); 63 63 64 for( unsignedi = 0; i < tthreads; i++) {64 for(int i = 0; i < tthreads; i++) { 65 65 thddata[i].self.post(); 66 66 fibre_join( threads[i], nullptr ); -
benchmark/readyQ/cycle.go
rb7fd9daf rf95634e 60 60 atomic.StoreInt32(&stop, 1) 61 61 end := time.Now() 62 d uration:= end.Sub(start)62 delta := 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) : % d\n", duration.Milliseconds())76 p.Printf("Duration (ms) : %f\n", delta.Seconds()); 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 uration.Seconds())82 p.Printf("ns per ops : %18.2f\n", float64(d uration.Nanoseconds()) / float64(global_counter))81 p.Printf("Ops per second : %18.2f\n", float64(global_counter) / delta.Seconds()) 82 p.Printf("ns per ops : %18.2f\n", float64(delta.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 uration.Seconds())86 p.Printf("ns per ops/procs : %18.2f\n", float64(d uration.Nanoseconds()) / (float64(global_counter) / float64(nprocs)))85 p.Printf("Ops/sec/procs : %18.2f\n", (float64(global_counter) / float64(nprocs)) / delta.Seconds()) 86 p.Printf("ns per ops/procs : %18.2f\n", float64(delta.Nanoseconds()) / (float64(global_counter) / float64(nprocs))) 87 87 88 88 } -
benchmark/readyQ/cycle.rs
rb7fd9daf rf95634e 46 46 47 47 let tthreads = nthreads * ring_size; 48 let exp = Arc::new(bench::BenchData::new(options, tthreads , None));48 let exp = Arc::new(bench::BenchData::new(options, tthreads)); 49 49 50 50 let s = (1000000 as u64).to_formatted_string(&Locale::en); -
benchmark/readyQ/locality.go
rb7fd9daf rf95634e 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 ( ms) : %f\n", delta.Milliseconds());288 p.Printf("Duration (s) : %f\n", delta.Seconds()); 289 289 p.Printf("Number of processors : %d\n", nprocs); 290 290 p.Printf("Number of threads : %d\n", nthreads); -
benchmark/readyQ/locality.rs
rb7fd9daf rf95634e 124 124 return (r as *mut MyData, true); 125 125 } 126 let got = self.ptr.compare_ exchange_weak(expected, ctx as *mut MyCtx as u64, Ordering::SeqCst, Ordering::SeqCst);127 if got == Ok(expected){126 let got = self.ptr.compare_and_swap(expected, ctx as *mut MyCtx as u64, Ordering::SeqCst); 127 if got == 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 , None));287 let exp = Arc::new(bench::BenchData::new(options, nprocs)); 288 288 let mut results = Result::new(); 289 289 -
benchmark/readyQ/transfer.cfa
rb7fd9daf rf95634e 39 39 Pause(); 40 40 if( (timeHiRes() - start) > 5`s ) { 41 print_stats_now( bench_cluster, CFA_STATS_READY_Q | CFA_STATS_IO );42 41 serr | "Programs has been blocked for more than 5 secs"; 43 42 exit(1); … … 111 110 cfa_option opt[] = { 112 111 BENCH_OPT, 113 { 'e', "exhaust", "Whether or not threads that have seen the new epoch should park instead of yielding.", exhaust, parse_yesno}112 { 'e', "exhaust", "Whether or not threads that have seen the new epoch should yield or park.", exhaust, parse_yesno} 114 113 }; 115 114 BENCH_OPT_PARSE("cforall transition benchmark"); … … 167 166 } 168 167 169 sout | "Duration (ms) : " | ws(3, 3, unit(eng((end - start)`dms)));168 sout | "Duration : " | ws(3, 3, unit(eng((end - start)`ds))) | 's'; 170 169 sout | "Number of processors : " | nprocs; 171 170 sout | "Number of threads : " | nthreads; -
benchmark/readyQ/transfer.cpp
rb7fd9daf rf95634e 173 173 } 174 174 175 std::cout << "Duration (ms) : " << to_miliseconds(end - start)<< std::endl;175 std::cout << "Duration : " << to_miliseconds(end - start) << "ms" << 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
rb7fd9daf rf95634e 80 80 } 81 81 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); 82 printf("Took %'ld ms\n", (end - start)`ms); 86 83 printf("Yields per second : %'18.2lf\n", ((double)global_counter) / (end - start)`s); 87 84 printf("ns per yields : %'18.2lf\n", ((double)(end - start)`ns) / global_counter); 85 printf("Total yields : %'15llu\n", global_counter); 88 86 printf("Yields per procs : %'15llu\n", global_counter / nprocs); 89 87 printf("Yields/sec/procs : %'18.2lf\n", (((double)global_counter) / nprocs) / (end - start)`s); -
benchmark/readyQ/yield.cpp
rb7fd9daf rf95634e 154 154 155 155 auto dur_nano = duration_cast<std::nano>(duration); 156 auto dur_dms = duration_cast<std::milli>(duration);157 156 158 printf("Duration (ms) : %'.2lf\n", dur_dms );157 std::cout << "Took " << duration << " s\n"; 159 158 printf("Total yields : %'15llu\n", global_counter ); 160 159 printf("Yields per procs : %'15llu\n", global_counter / nprocs ); -
benchmark/rmit.py
rb7fd9daf rf95634e 16 16 import random 17 17 import re 18 import socket19 18 import subprocess 20 19 import sys … … 96 95 return nopts 97 96 98 # returns the first option with key 'opt'99 def search_option(action, opt):100 i = 0101 while i < len(action):102 if action[i] == opt:103 i += 1104 if i != len(action):105 return action[i]106 i += 1107 108 return None109 110 97 def actions_eta(actions): 111 98 time = 0 112 99 for a in actions: 113 o = search_option(a, '-d') 114 if o : 115 time += int(o) 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 116 107 return time 117 118 taskset_maps = None119 120 def init_taskset_maps():121 global taskset_maps122 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 True135 136 print("Warning unknown host '{}', disable taskset usage".format(host), file=sys.stderr)137 return False138 139 140 def settaskset_one(action):141 o = search_option(action, '-p')142 if not o:143 return action144 try:145 oi = int(o)146 except ValueError:147 return action148 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 action156 157 def settaskset(actions):158 return [settaskset_one(a) for a in actions]159 108 160 109 if __name__ == "__main__": … … 166 115 parser.add_argument('--file', nargs='?', type=argparse.FileType('w'), default=sys.stdout) 167 116 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')169 117 parser.add_argument('command', metavar='command', type=str, nargs=1, help='the command prefix to run') 170 118 parser.add_argument('candidates', metavar='candidates', type=str, nargs='*', help='the candidate suffix to run') … … 222 170 223 171 # ================================================================================ 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 172 # Figure out all the combinations to run 239 173 if options.list: 240 174 for a in actions: … … 246 180 # Prepare to run 247 181 182 # find expected time 183 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 248 186 random.shuffle(actions) 249 187 … … 253 191 first = True 254 192 for i, a in enumerate(actions): 255 sa = " ".join(a [3:] if withtaskset else a)193 sa = " ".join(a) 256 194 if first: 257 195 first = False … … 270 208 match = re.search("^(.*):(.*)$", s) 271 209 if match: 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])) 210 fields[match.group(1).strip()] = float(match.group(2).strip().replace(',','')) 211 212 options.file.write(json.dumps([a[0][2:], sa, fields])) 278 213 options.file.flush() 279 214 -
doc/theses/andrew_beach_MMath/Makefile
rb7fd9daf rf95634e 31 31 32 32 # The main rule, it does all the tex/latex processing. 33 ${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} termhandle.pstex resumhandle.pstexMakefile | ${BUILD}33 ${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} 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}/$@_t46 42 47 43 # Step through dvi & postscript to handle xfig specials. -
doc/theses/andrew_beach_MMath/code/CondCatch.java
rb7fd9daf rf95634e 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 8 22 private static long loop(int times) { 9 23 long startTime = System.nanoTime(); 10 24 for (int count = 0 ; count < times ; ++count) { 11 25 try { 12 try { 13 throw new EmptyException(); 14 } catch (EmptyException exc) { 15 if (!should_catch) { 16 throw exc; 17 } 18 } 26 cond_catch(); 19 27 } catch (EmptyException exc) { 20 28 // ... … … 38 46 39 47 long time = loop(times); 40 System.out. format("Run-Time (s): %.1f%n", time / 1_000_000_000.);48 System.out.println("Run-Time (ns): " + time); 41 49 } 42 50 } -
doc/theses/andrew_beach_MMath/code/ThrowEmpty.java
rb7fd9daf rf95634e 39 39 40 40 long time = loop(times, total_frames); 41 System.out. format("Run-Time (s): %.1f%n", time / 1_000_000_000.);41 System.out.println("Run-Time (ns): " + time); 42 42 } 43 43 } -
doc/theses/andrew_beach_MMath/code/ThrowFinally.java
rb7fd9daf rf95634e 44 44 45 45 long time = loop(times, total_frames); 46 System.out. format("Run-Time (s): %.1f%n", time / 1_000_000_000.);46 System.out.println("Run-Time (ns): " + time); 47 47 } 48 48 } -
doc/theses/andrew_beach_MMath/code/ThrowOther.java
rb7fd9daf rf95634e 52 52 53 53 long time = loop(times, total_frames); 54 System.out. format("Run-Time (s): %.1f%n", time / 1_000_000_000.);54 System.out.println("Run-Time (ns): " + time); 55 55 } 56 56 } -
doc/theses/andrew_beach_MMath/code/cond-catch.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.h fa> // strto5 #include <stdlib.h> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 9 10 10 11 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 } 11 24 12 25 int main(int argc, char * argv[]) { 13 26 unsigned int times = 1; 14 27 if (1 < argc) { 15 times = strto (argv[1], 0p, 10);28 times = strtol(argv[1], 0p, 10); 16 29 } 17 30 if (2 < argc) { 18 should_catch = (unsigned int)strto(argv[2], 0p, 2);31 should_catch = strtol(argv[2], 0p, 10); 19 32 } 20 33 … … 22 35 for (unsigned int count = 0 ; count < times ; ++count) { 23 36 try { 24 throw (empty_exception){&empty_vt}; 25 } catch (empty_exception * exc ; should_catch) { 26 asm volatile ("# catch block (conditional)"); 37 cond_catch(); 27 38 } catch (empty_exception * exc) { 28 39 asm volatile ("# catch block (unconditional)"); … … 30 41 } 31 42 Time end_time = timeHiRes(); 32 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);43 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 33 44 } -
doc/theses/andrew_beach_MMath/code/cond-catch.cpp
rb7fd9daf rf95634e 4 4 #include <exception> 5 5 #include <iostream> 6 #include <iomanip>7 6 8 using namespace std;9 7 using namespace std::chrono; 10 8 … … 12 10 13 11 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 } 14 27 15 28 int main(int argc, char * argv[]) { … … 25 38 for (unsigned int count = 0 ; count < times ; ++count) { 26 39 try { 27 try { 28 throw EmptyException(); 29 } catch (EmptyException & exc) { 30 if (!should_catch) { 31 throw; 32 } 33 asm volatile ("# catch block (conditional)"); 34 } 40 cond_catch(); 35 41 } catch (EmptyException &) { 36 42 asm volatile ("# catch block (unconditional)"); … … 39 45 time_point<steady_clock> end_time = steady_clock::now(); 40 46 nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time); 41 cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. <<endl;47 std::cout << "Run-Time (ns): " << duration.count() << std::endl; 42 48 } -
doc/theses/andrew_beach_MMath/code/cond-fixup.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 9 10 10 11 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 } 11 24 12 25 int main(int argc, char * argv[]) { 13 26 unsigned int times = 1; 14 27 if (1 < argc) { 15 times = strto (argv[1], 0p, 10);28 times = strtol(argv[1], 0p, 10); 16 29 } 17 30 if (2 < argc) { 18 should_catch = (unsigned int)strto(argv[2], 0p, 2);31 should_catch = strtol(argv[2], 0p, 10); 19 32 } 20 33 … … 22 35 for (unsigned int count = 0 ; count < times ; ++count) { 23 36 try { 24 throwResume (empty_exception){&empty_vt}; 25 } catchResume (empty_exception * exc ; should_catch) { 26 asm volatile ("# fixup block (conditional)"); 37 cond_catch(); 27 38 } catchResume (empty_exception * exc) { 28 39 asm volatile ("# fixup block (unconditional)"); … … 30 41 } 31 42 Time end_time = timeHiRes(); 32 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);43 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 33 44 } -
doc/theses/andrew_beach_MMath/code/resume-detor.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 9 10 10 11 struct WithDestructor {}; … … 16 17 void unwind_destructor(unsigned int frames) { 17 18 if (frames) { 19 18 20 WithDestructor object; 19 21 unwind_destructor(frames - 1); … … 27 29 unsigned int total_frames = 1; 28 30 if (1 < argc) { 29 times = strto (argv[1], 0p, 10);31 times = strtol(argv[1], 0p, 10); 30 32 } 31 33 if (2 < argc) { 32 total_frames = strto (argv[2], 0p, 10);34 total_frames = strtol(argv[2], 0p, 10); 33 35 } 34 36 … … 42 44 } 43 45 Time end_time = timeHiRes(); 44 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);46 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 45 47 } -
doc/theses/andrew_beach_MMath/code/resume-empty.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 7 EHM_EXCEPTION(empty_exception)(); 9 8 10 void nounwind_empty(unsigned int frames) { 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 10 11 void unwind_empty(unsigned int frames) { 11 12 if (frames) { 12 nounwind_empty(frames - 1); 13 if ( frames == -1 ) printf( "42" ); // prevent recursion optimizations 13 unwind_empty(frames - 1); 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 (argv[1], 0p, 10);23 times = strtol(argv[1], 0p, 10); 24 24 } 25 25 if (2 < argc) { 26 total_frames = strto (argv[2], 0p, 10);26 total_frames = strtol(argv[2], 0p, 10); 27 27 } 28 28 29 29 Time start_time = timeHiRes(); 30 for ( unsignedint count = 0 ; count < times ; ++count) {30 for (int count = 0 ; count < times ; ++count) { 31 31 try { 32 nounwind_empty(total_frames);32 unwind_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 ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);38 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 39 39 } -
doc/theses/andrew_beach_MMath/code/resume-finally.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 9 10 10 11 void unwind_finally(unsigned int frames) { … … 24 25 unsigned int total_frames = 1; 25 26 if (1 < argc) { 26 times = strto (argv[1], 0p, 10);27 times = strtol(argv[1], 0p, 10); 27 28 } 28 29 if (2 < argc) { 29 total_frames = strto (argv[2], 0p, 10);30 total_frames = strtol(argv[2], 0p, 10); 30 31 } 31 32 … … 39 40 } 40 41 Time end_time = timeHiRes(); 41 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);42 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 42 43 } -
doc/theses/andrew_beach_MMath/code/resume-other.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 9 exception not_raised_exception; 7 EHM_EXCEPTION(empty_exception)(); 10 8 11 void nounwind_other(unsigned int frames) { 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 10 11 EHM_EXCEPTION(not_raised_exception)(); 12 13 void unwind_other(unsigned int frames) { 12 14 if (frames) { 13 15 try { 14 nounwind_other(frames - 1);16 unwind_other(frames - 1); 15 17 } catchResume (not_raised_exception *) { 16 18 asm volatile ("# fixup block (stack)"); … … 25 27 unsigned int total_frames = 1; 26 28 if (1 < argc) { 27 times = strto (argv[1], 0p, 10);29 times = strtol(argv[1], 0p, 10); 28 30 } 29 31 if (2 < argc) { 30 total_frames = strto (argv[2], 0p, 10);32 total_frames = strtol(argv[2], 0p, 10); 31 33 } 32 34 … … 34 36 for (int count = 0 ; count < times ; ++count) { 35 37 try { 36 nounwind_other(total_frames);38 unwind_other(total_frames); 37 39 } catchResume (empty_exception *) { 38 40 asm volatile ("# fixup block (base)"); … … 40 42 } 41 43 Time end_time = timeHiRes(); 42 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);44 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 43 45 } -
doc/theses/andrew_beach_MMath/code/run.sh
rb7fd9daf rf95634e 1 1 #!/usr/bin/env bash 2 2 3 readonly ALL_TESTS=( raise-{empty,detor,finally,other} try-{catch,finally} \4 cond-match-{all,none} fixup-{empty,other})3 readonly ALL_TESTS=(cond-match-{all,none} cross-{catch,finally} \ 4 raise-{detor,empty,finally,other}) 5 5 6 6 gen-file-name() ( … … 18 18 ) 19 19 20 readonly N=${1:- 1}20 readonly N=${1:-5} 21 21 readonly OUT_FILE=$(gen-file-name ${2:-run-%-$N}) 22 22 -
doc/theses/andrew_beach_MMath/code/test.sh
rb7fd9daf rf95634e 4 4 # test.sh LANGUAGE TEST 5 5 # Run the TEST in LANGUAGE. 6 # test.sh -a7 # Build all tests.8 6 # test.sh -b SOURCE_FILE... 9 7 # Build a test from SOURCE_FILE(s). 10 # test.sh -c11 # Clean all executables.12 8 # test.sh -v LANGUAGE TEST FILE 13 9 # View the result from TEST in LANGUAGE stored in FILE. 14 10 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 11 readonly ITERATIONS=1000000 # 1 000 000, one million 24 12 readonly STACK_HEIGHT=100 25 13 … … 35 23 case "$1" in 36 24 *.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}" 25 # Requires a symbolic link. 26 mmake "${1%.cfa}" "$1" ./cfa -DNDEBUG -nodebug -O3 "$1" -o "${1%.cfa}" 43 27 ;; 44 28 *.cpp) 45 mmake "${1%.cpp}-cpp" "$1" g++ -10-DNDEBUG -O3 "$1" -o "${1%.cpp}-cpp"29 mmake "${1%.cpp}-cpp" "$1" g++ -DNDEBUG -O3 "$1" -o "${1%.cpp}-cpp" 46 30 ;; 47 31 *.java) … … 55 39 ) 56 40 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 41 if [ "-b" = "$1" ]; then 63 42 for file in "${@:2}"; do 64 43 build $file 65 44 done 66 45 exit 0 67 elif [ "-c" = "$1" ]; then68 rm $(basename -s ".cfa" -a *.cfa)69 rm $(basename -s ".cpp" -a *.cpp)70 rm *-cpp71 rm *.class72 exit 073 46 elif [ "-v" = "$1" -a 4 = "$#" ]; then 74 TEST_LANG="$2"75 TEST_CASE="$3"76 VIEW_FILE="$4"47 TEST_LANG="$2" 48 TEST_CASE="$3" 49 VIEW_FILE="$4" 77 50 elif [ 2 -eq "$#" ]; then 78 51 TEST_LANG="$1" … … 90 63 91 64 case "$TEST_CASE" in 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" 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" 98 92 ;; 99 93 raise-detor) 100 CFAT="./throw-detor $ITER S_1M$STACK_HEIGHT"101 CFAR="./resume-detor $ITER S_10M$STACK_HEIGHT"102 CPP="./throw-detor-cpp $ITER S_1M$STACK_HEIGHT"94 CFAT="./throw-detor $ITERATIONS $STACK_HEIGHT" 95 CFAR="./resume-detor $ITERATIONS $STACK_HEIGHT" 96 CPP="./throw-detor-cpp $ITERATIONS $STACK_HEIGHT" 103 97 JAVA=unsupported 104 98 PYTHON=unsupported 105 99 ;; 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 ;; 106 107 raise-finally) 107 CFAT="./throw-finally $ITER S_1M$STACK_HEIGHT"108 CFAR="./resume-finally $ITER S_10M$STACK_HEIGHT"108 CFAT="./throw-finally $ITERATIONS $STACK_HEIGHT" 109 CFAR="./resume-finally $ITERATIONS $STACK_HEIGHT" 109 110 CPP=unsupported 110 JAVA="java ThrowFinally $ITER S_1M$STACK_HEIGHT"111 PYTHON="./throw -finally.py $ITERS_1M$STACK_HEIGHT"111 JAVA="java ThrowFinally $ITERATIONS $STACK_HEIGHT" 112 PYTHON="./throw_finally.py $ITERATIONS $STACK_HEIGHT" 112 113 ;; 113 114 raise-other) 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" 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" 161 120 ;; 162 121 *) … … 181 140 182 141 if [ -n "$VIEW_FILE" ]; then 183 grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time.*: !!;T;p'184 exit142 grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time (ns): !!;T;p' 143 exit 185 144 fi 186 145 -
doc/theses/andrew_beach_MMath/code/throw-detor.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 9 10 10 11 struct WithDestructor {}; … … 27 28 unsigned int total_frames = 1; 28 29 if (1 < argc) { 29 times = strto (argv[1], 0p, 10);30 times = strtol(argv[1], 0p, 10); 30 31 } 31 32 if (2 < argc) { 32 total_frames = strto (argv[2], 0p, 10);33 total_frames = strtol(argv[2], 0p, 10); 33 34 } 34 35 … … 42 43 } 43 44 Time end_time = timeHiRes(); 44 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);45 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 45 46 } -
doc/theses/andrew_beach_MMath/code/throw-detor.cpp
rb7fd9daf rf95634e 4 4 #include <exception> 5 5 #include <iostream> 6 #include <iomanip>7 6 8 using namespace std;9 7 using namespace std::chrono; 10 8 … … 46 44 time_point<steady_clock> end_time = steady_clock::now(); 47 45 nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time); 48 cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. <<endl;46 std::cout << "Run-Time (ns): " << duration.count() << std::endl; 49 47 } -
doc/theses/andrew_beach_MMath/code/throw-empty.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 7 EHM_EXCEPTION(empty_exception)(); 8 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 9 10 10 11 void unwind_empty(unsigned int frames) { 11 12 if (frames) { 12 13 unwind_empty(frames - 1); 13 if ( frames == -1 ) printf( "42" ); // prevent recursion optimizations14 14 } else { 15 15 throw (empty_exception){&empty_vt}; … … 21 21 unsigned int total_frames = 1; 22 22 if (1 < argc) { 23 times = strto (argv[1], 0p, 10);23 times = strtol(argv[1], 0p, 10); 24 24 } 25 25 if (2 < argc) { 26 total_frames = strto (argv[2], 0p, 10);26 total_frames = strtol(argv[2], 0p, 10); 27 27 } 28 28 … … 36 36 } 37 37 Time end_time = timeHiRes(); 38 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);38 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 39 39 } -
doc/theses/andrew_beach_MMath/code/throw-empty.cpp
rb7fd9daf rf95634e 1 1 // Throw Across Empty Function 2 2 #include <chrono> 3 #include <cstdio>4 3 #include <cstdlib> 5 4 #include <exception> 6 5 #include <iostream> 7 #include <iomanip>8 6 9 using namespace std;10 7 using namespace std::chrono; 11 8 … … 15 12 if (frames) { 16 13 unwind_empty(frames - 1); 17 if (-1 == frames) printf("~");18 14 } else { 19 15 throw (EmptyException){}; … … 41 37 time_point<steady_clock> end_time = steady_clock::now(); 42 38 nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time); 43 cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. <<endl;39 std::cout << "Run-Time (ns): " << duration.count() << std::endl; 44 40 } -
doc/theses/andrew_beach_MMath/code/throw-finally.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 7 EHM_EXCEPTION(empty_exception)(); 9 8 10 unsigned int frames; // use global because of gcc thunk problem 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 11 10 12 void unwind_finally(unsigned int dummy) {11 void unwind_finally(unsigned int frames) { 13 12 if (frames) { 14 frames -= 1;15 13 try { 16 unwind_finally( 42);14 unwind_finally(frames - 1); 17 15 } finally { 18 16 asm volatile ("# finally block"); 19 17 } 20 18 } else { 21 dummy = 42;22 19 throw (empty_exception){&empty_vt}; 23 20 } … … 28 25 unsigned int total_frames = 1; 29 26 if (1 < argc) { 30 times = strto (argv[1], 0p, 10);27 times = strtol(argv[1], 0p, 10); 31 28 } 32 29 if (2 < argc) { 33 total_frames = strto (argv[2], 0p, 10);30 total_frames = strtol(argv[2], 0p, 10); 34 31 } 35 frames = total_frames;36 32 37 33 Time start_time = timeHiRes(); 38 34 for (int count = 0 ; count < times ; ++count) { 39 35 try { 40 unwind_finally( 42);36 unwind_finally(total_frames); 41 37 } catch (empty_exception *) { 42 38 asm volatile ("# catch block"); … … 44 40 } 45 41 Time end_time = timeHiRes(); 46 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);42 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 47 43 } -
doc/theses/andrew_beach_MMath/code/throw-other.cfa
rb7fd9daf rf95634e 3 3 #include <exception.hfa> 4 4 #include <fstream.hfa> 5 #include <stdlib.hfa> // strto5 #include <stdlib.hfa> 6 6 7 exception empty_exception; 8 vtable(empty_exception) empty_vt; 9 exception not_raised_exception; 7 EHM_EXCEPTION(empty_exception)(); 10 8 11 unsigned int frames; // use global because of gcc thunk problem 9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt); 12 10 13 void unwind_other(unsigned int dummy) { 11 EHM_EXCEPTION(not_raised_exception)(); 12 13 void unwind_other(unsigned int frames) { 14 14 if (frames) { 15 frames -= 1;16 15 try { 17 unwind_other( 42);16 unwind_other(frames - 1); 18 17 } catch (not_raised_exception *) { 19 18 asm volatile ("# catch block (stack)"); 20 19 } 21 20 } else { 22 dummy = 42;23 21 throw (empty_exception){&empty_vt}; 24 22 } … … 29 27 unsigned int total_frames = 1; 30 28 if (1 < argc) { 31 times = strto (argv[1], 0p, 10);29 times = strtol(argv[1], 0p, 10); 32 30 } 33 31 if (2 < argc) { 34 total_frames = strto (argv[2], 0p, 10);32 total_frames = strtol(argv[2], 0p, 10); 35 33 } 36 frames = total_frames;37 34 38 35 Time start_time = timeHiRes(); 39 36 for (int count = 0 ; count < times ; ++count) { 40 37 try { 41 unwind_other( 42);38 unwind_other(total_frames); 42 39 } catch (empty_exception *) { 43 40 asm volatile ("# catch block (base)"); … … 45 42 } 46 43 Time end_time = timeHiRes(); 47 sout | "Run-Time ( s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);44 sout | "Run-Time (ns): " | (end_time - start_time)`ns; 48 45 } -
doc/theses/andrew_beach_MMath/code/throw-other.cpp
rb7fd9daf rf95634e 4 4 #include <exception> 5 5 #include <iostream> 6 #include <iomanip>7 6 8 using namespace std;9 7 using namespace std::chrono; 10 8 … … 45 43 time_point<steady_clock> end_time = steady_clock::now(); 46 44 nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time); 47 cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. <<endl;45 std::cout << "Run-Time (ns): " << duration.count() << std::endl; 48 46 } -
doc/theses/andrew_beach_MMath/conclusion.tex
rb7fd9daf rf95634e 1 1 \chapter{Conclusion} 2 \label{c:conclusion}3 2 % Just a little knot to tie the paper together. 4 3 5 In the previous chapters ,this thesis presents the design and implementation4 In the previous chapters this thesis presents the design and implementation 6 5 of \CFA's exception handling mechanism (EHM). 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. 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. 13 9 14 10 The resulting features cover all of the major use cases of the most popular 15 11 termination EHMs of today, along with reintroducing resumption exceptions and 16 creating some new features that fit with \CFA's larger programming patterns, 17 such as virtuals independent of traditional objects. 12 creating some new features that fix with \CFA's larger programming patterns. 18 13 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. 14 The implementation has been tested and compared to other implementations. 23 15 The results, while not cutting edge, are good enough for prototyping, which 24 is \CFA's currentstage of development.16 is \CFA's stage of development. 25 17 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. 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. -
doc/theses/andrew_beach_MMath/exception-layout.fig
rb7fd9daf rf95634e 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 0135 1080 2700 585 Fixed Header\00131 4 0 0 50 -1 0 12 0.0000 0 135 1575540 990 Cforall Information\00132 4 0 0 50 -1 0 12 0.0000 0 180 1695540 585 _Unwind_Exception\00133 4 0 0 50 -1 0 12 0.0000 0 180 1245540 1530 User Exception\00134 4 0 0 50 -1 0 12 0.0000 0 180 11852655 1530 Variable Body\00135 4 0 0 50 -1 0 12 0.0000 0 165 1110 2655 1215 (Fixed Offset)\00130 4 0 0 50 -1 0 12 0.0000 4 135 1080 2700 585 Fixed Header\001 31 4 0 0 50 -1 0 12 0.0000 4 135 1710 540 990 Cforall Information\001 32 4 0 0 50 -1 0 12 0.0000 4 165 1530 540 585 _Unwind_Exception\001 33 4 0 0 50 -1 0 12 0.0000 4 165 1260 540 1530 User Exception\001 34 4 0 0 50 -1 0 12 0.0000 4 165 1170 2655 1530 Variable Body\001 35 4 0 0 50 -1 0 12 0.0000 4 165 1260 2655 1215 (Fixed Offset)\001 -
doc/theses/andrew_beach_MMath/existing.tex
rb7fd9daf rf95634e 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, 10 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 allowing programmers to learn \CFA on an as-needed basis. 11 10 12 11 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{ cfa}20 char i; int i; double i;21 int f(); double f();22 void g( int ); void g( double );23 \end{ cfa}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} 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 pointer-types,but each48 Reference-types are written the same way as a pointer-type but each 49 49 asterisk (@*@) is replaced with a ampersand (@&@); 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 50 this includes cv-qualifiers and multiple levels of reference. 51 52 Generally, references act like pointers with an implicate dereferencing 54 53 operation added to each use of the variable. 55 54 These automatic dereferences may be disabled with the address-of operator … … 64 63 int && rri = ri; 65 64 rri = 3; 66 &ri = &j; 65 &ri = &j; // rebindable 67 66 ri = 5; 68 67 \end{cfa} … … 80 79 \end{minipage} 81 80 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. 81 References are intended for pointer situations where dereferencing is the common usage, 82 \ie the value is more important than the pointer. 85 83 Mutable references may be assigned to by converting them to a pointer 86 with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above .84 with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above 87 85 88 86 \section{Operators} 89 87 90 88 \CFA implements operator overloading by providing special names, where 91 operator expressions are translated into function calls using these names.89 operator usages are translated into function calls using these names. 92 90 An operator name is created by taking the operator symbols and joining them with 93 91 @?@s to show where the arguments go. 94 92 For example, 95 93 infixed multiplication is @?*?@, while prefix dereference is @*?@. 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. 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. 100 97 \begin{cfa} 101 98 point ?+?(point a, point b) { return point{a.x + b.x, a.y + b.y}; } … … 105 102 } 106 103 \end{cfa} 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. 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. 113 114 114 115 %\subsection{Constructors and Destructors} 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++. 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++. 120 123 121 124 The special name for a constructor is @?{}@, which comes from the … … 126 129 struct Example { ... }; 127 130 void ?{}(Example & this) { ... } 128 {129 Example a;130 Example b = {};131 }132 131 void ?{}(Example & this, char first, int num) { ... } 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 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} 143 142 % I don't like the \^{} symbol but $^\wedge$ isn't better. 144 143 Similarly, destructors use the special name @^?{}@ (the @^@ has no special 145 144 meaning). 145 % These are a normally called implicitly called on a variable when it goes out 146 % of scope. They can be called explicitly as well. 146 147 \begin{cfa} 147 148 void ^?{}(Example & this) { ... } 148 149 { 149 Example d; 150 ^?{}(d); 151 152 Example e; 153 } // Implicit call of ^?{}(e); 150 Example e; // implicit constructor call 151 ^?{}(e); // explicit destructor call 152 ?{}(e); // explicit constructor call 153 } // implicit destructor call 154 154 \end{cfa} 155 155 … … 203 203 do_twice(i); 204 204 \end{cfa} 205 Any valuewith a type fulfilling the assertion may be passed as an argument to205 Any object 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 @quadruple@took 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 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 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 231 232 instead of the individual constraints. 232 233 \begin{cfa} … … 242 243 functions and variables, and are usually used to create a shorthand for, and 243 244 give descriptive names to, common groupings of assertions describing a certain 244 functionality, like @sum mable@, @listable@, \etc.245 functionality, like @sumable@, @listable@, \etc. 245 246 246 247 Polymorphic structures and unions are defined by qualifying an aggregate type 247 248 with @forall@. The type variables work the same except they are used in field 248 declarations instead of parameters, returns and local variable declarations.249 declarations instead of parameters, returns, and local variable declarations. 249 250 \begin{cfa} 250 251 forall(dtype T) … … 252 253 node(T) * next; 253 254 T * data; 254 } ;255 } 255 256 node(int) inode; 256 257 \end{cfa} … … 262 263 263 264 \section{Control Flow} 264 \CFA has a number of advanced control-flow features: @generator@, @coroutine@, 265 @monitor@, @mutex@ parameters, and @thread@. 265 \CFA has a number of advanced control-flow features: @generator@, @coroutine@, @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. 273 Instead, 272 required to finish execution when control is handed back to the caller. Instead 274 273 they may suspend execution at any time and be resumed later at the point of 275 last suspension. 276 Coroutine 274 last suspension. (Generators are stackless and coroutines are stackful.) These 277 275 types are not concurrent but share some similarities along with common 278 underpinnings, so they are combined with the \CFA threading library. 279 % I had mention of generators, but they don't actually matter here. 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. 280 279 281 280 In \CFA, a coroutine is created using the @coroutine@ keyword, which is an … … 294 293 }; 295 294 CountUp countup; 295 for (10) sout | resume(countup).next; // print 10 values 296 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} 300 \begin{cfa}[numbers=left,numberstyle=\scriptsize\sf] 301 301 void main(CountUp & this) { 302 for (unsigned int next = 0 ; true ; ++next) {303 this.next = next;302 for (unsigned int up = 0;; ++up) { 303 this.next = up; 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 caller310 without terminating the coroutine's function .309 @suspend@ statement is used to return execution to the coroutine's resumer 310 without terminating the coroutine's function(s). 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 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. 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@. 329 321 330 322 \subsection{Monitor and Mutex Parameter} 331 Concurrency does not guarantee ordering; without ordering ,results are323 Concurrency does not guarantee ordering; without ordering results are 332 324 non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@ 333 325 (mutual exclusion) parameters. A monitor is another kind of aggregate, where … … 335 327 @mutex@ parameters. 336 328 337 A function that requires deterministic (ordered) execution acquires mutual329 A function that requires deterministic (ordered) execution, acquires mutual 338 330 exclusion on a monitor object by qualifying an object reference parameter with 339 the @mutex@ qualifier.340 \begin{ cfa}341 void example(MonitorA & mutex argA, MonitorB & mutexargB);342 \end{ cfa}331 @mutex@. 332 \begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}] 333 void example(MonitorA & @mutex@ argA, MonitorB & @mutex@ argB); 334 \end{lstlisting} 343 335 When the function is called, it implicitly acquires the monitor lock for all of 344 336 the mutex parameters without deadlock. This semantics means all functions with … … 347 339 348 340 \subsection{Thread} 349 Functions, generators and coroutines are sequential,so there is only a single341 Functions, generators, and coroutines are sequential so there is only a single 350 342 (but potentially sophisticated) execution path in a program. Threads introduce 351 343 multiple execution paths that continue independently. 352 344 353 345 For threads to work safely with objects requires mutual exclusion using 354 monitors and mutex parameters. For threads to work safely with other threads 346 monitors and mutex parameters. For threads to work safely with other threads, 355 347 also requires mutual exclusion in the form of a communication rendezvous, which 356 348 also supports internal synchronization as for mutex objects. For exceptions, … … 370 362 { 371 363 StringWorker stringworker; // fork thread running in "main" 372 } // Implicit call to join(stringworker), waits for completion.364 } // implicitly join with thread / wait for completion 373 365 \end{cfa} 374 366 The thread main is where a new thread starts execution after a fork operation -
doc/theses/andrew_beach_MMath/features.tex
rb7fd9daf rf95634e 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, along with some other information, 33 in a handler. 32 exception. This code is given, with some other information, in a handler. 34 33 35 34 A handler has three common features: the previously mentioned user code, a 36 region of code it guards and an exception label/condition that matches37 againstthe raised exception.35 region of code it guards, and an exception label/condition that matches 36 the raised exception. 38 37 Only raises inside the guarded region and raising exceptions that match the 39 38 label can be handled by a given handler. … … 42 41 43 42 The @try@ statements of \Cpp, Java and Python are common examples. All three 44 also show another common feature of handlers: they are grouped by the guarded 45 region. 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} 46 53 47 54 \subsection{Propagation} 48 55 After an exception is raised comes what is usually the biggest step for the 49 EHM: finding and setting up the handler for execution. 50 The propagation from raise to 56 EHM: finding and setting up the handler for execution. The propagation from raise to 51 57 handler can be broken up into three different tasks: searching for a handler, 52 58 matching against the handler and installing the handler. … … 54 60 \paragraph{Searching} 55 61 The EHM begins by searching for handlers that might be used to handle 56 the exception. 57 The search will findhandlers that have the raise site in their guarded62 the exception. The search is restricted to 63 handlers that have the raise site in their guarded 58 64 region. 59 65 The search includes handlers in the current function, as well as any in … … 61 67 62 68 \paragraph{Matching} 63 Each handler found is with the raised exception. The exception69 Each handler found is matched with the raised exception. The exception 64 70 label defines a condition that is used with the exception and decides if 65 71 there is a match or not. 66 %67 72 In languages where the first match is used, this step is intertwined with 68 73 searching; a match check is performed immediately after the search finds … … 79 84 different course of action for this case. 80 85 This situation only occurs with unchecked exceptions as checked exceptions 81 (such as in Java) can make the guarantee.86 (such as in Java) are guaranteed to find a matching handler. 82 87 The unhandled action is usually very general, such as aborting the program. 83 88 84 89 \paragraph{Hierarchy} 85 90 A common way to organize exceptions is in a hierarchical structure. 86 This pattern comes from object-orient ed languages where the91 This pattern comes from object-orientated languages where the 87 92 exception hierarchy is a natural extension of the object hierarchy. 88 93 … … 93 98 A handler labeled with any given exception can handle exceptions of that 94 99 type or any child type of that exception. The root of the exception hierarchy 95 (here \code{C}{exception}) acts as a catch-all, leaf types catch single types 100 (here \code{C}{exception}) acts as a catch-all, leaf types catch single types, 96 101 and the exceptions in the middle can be used to catch different groups of 97 102 related exceptions. 98 103 99 104 This system has some notable advantages, such as multiple levels of grouping, 100 the ability for libraries to add new exception types and the isolation105 the ability for libraries to add new exception types, and the isolation 101 106 between different sub-hierarchies. 102 107 This design is used in \CFA even though it is not a object-orientated 103 language ,so different tools are used to create the hierarchy.108 language; so different tools are used to create the hierarchy. 104 109 105 110 % Could I cite the rational for the Python IO exception rework? … … 119 124 from the raise to the handler and back again. 120 125 So far, only communication of the exception's identity is covered. 121 A common communication method for adding information to an exception 122 is putting fields into the exception instance 126 A common communication method for passing more information is putting fields into the exception instance 123 127 and giving the handler access to them. 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. 128 Using reference fields pointing to data at the raise location allows data to be 129 passed in both directions. 128 130 129 131 \section{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 132 \label{s:Virtuals} 137 133 Virtual types and casts are not part of \CFA's EHM nor are they required for 138 134 an EHM. 139 135 However, one of the best ways to support an exception hierarchy 140 136 is via a virtual hierarchy and dispatch system. 141 Ideally, the virtual system would have been part of \CFA before the work137 Ideally, the virtual system should have been part of \CFA before the work 142 138 on exception handling began, but unfortunately it was not. 143 139 Hence, only the features and framework needed for the EHM were 144 designed and implemented for this thesis. 145 Other features were considered to ensure that 140 designed and implemented for this thesis. Other features were considered to ensure that 146 141 the structure could accommodate other desirable features in the future 147 142 but are not implemented. 148 143 The rest of this section only discusses the implemented subset of the 149 virtual system design.144 virtual-system design. 150 145 151 146 The virtual system supports multiple ``trees" of types. Each tree is … … 154 149 number of children. 155 150 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} 156 164 % A type's ancestors are its parent and its parent's ancestors. 157 165 % The root type has no ancestors. 158 166 % A type's descendants are its children and its children's descendants. 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. 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. 238 204 This capability means it is impossible to pick a single set of functions 239 205 that represent a type's implementation across a program. … … 242 208 type. A user can define virtual tables that are filled in at their 243 209 declaration and given a name. Anywhere that name is visible, even if it is 244 defined locally inside a function (although in this case the user must ensure245 it outlives any objects that use it), it can be used.210 defined locally inside a function \PAB{What does this mean? (although that means it does not have a 211 static lifetime)}, it can be used. 246 212 Specifically, a virtual type is ``bound" to a virtual table that 247 213 sets the virtual members for that object. The virtual members can be accessed 248 214 through the object. 249 215 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 216 While much of the virtual infrastructure is created, it is currently only used 276 217 internally for exception handling. The only user-level feature is the virtual 277 218 cast, which is the same as the \Cpp \code{C++}{dynamic_cast}. … … 282 223 Note, the syntax and semantics matches a C-cast, rather than the function-like 283 224 \Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be 284 pointers to virtual types.225 a pointer to a virtual type. 285 226 The cast dynamically checks if the @EXPRESSION@ type is the same or a sub-type 286 227 of @TYPE@, and if true, returns a pointer to the 287 228 @EXPRESSION@ object, otherwise it returns @0p@ (null pointer). 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 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 335 237 is the base trait all exceptions need to match. 336 238 \begin{cfa} … … 339 241 }; 340 242 \end{cfa} 341 The trait is defined over two types :the exception type and the virtual table243 The trait is defined over two types, the exception type and the virtual table 342 244 type. Each exception type should have a single virtual table type. 343 245 There are no actual assertions in this trait because the trait system … … 345 247 completing the virtual system). The imaginary assertions would probably come 346 248 from a trait defined by the virtual system, and state that the exception 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. 249 is a virtual type, is a descendant of @exception_t@ (the base exception type), 250 and note its virtual table type. 350 251 351 252 % I did have a note about how it is the programmer's responsibility to make … … 365 266 }; 366 267 \end{cfa} 367 Both traits ensure a pair of types is an exception type and368 its virtual tabletype,268 Both traits ensure a pair of types are an exception type, its virtual table 269 type, 369 270 and defines one of the two default handlers. The default handlers are used 370 as fallbacks and are discussed in detail in \ autoref{s:ExceptionHandling}.271 as fallbacks and are discussed in detail in \vref{s:ExceptionHandling}. 371 272 372 273 However, all three of these traits can be tricky to use directly. 373 274 While there is a bit of repetition required, 374 275 the largest issue is that the virtual table type is mangled and not in a user 375 facing way. So ,these three macros are provided to wrap these traits to276 facing way. So these three macros are provided to wrap these traits to 376 277 simplify referring to the names: 377 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@.278 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@, and @IS_RESUMPTION_EXCEPTION@. 378 279 379 280 All three take one or two arguments. The first argument is the name of the … … 381 282 The second (optional) argument is a parenthesized list of polymorphic 382 283 arguments. This argument is only used with polymorphic exceptions and the 383 list is passed to both types.284 list is be passed to both types. 384 285 In the current set-up, the two types always have the same polymorphic 385 arguments ,so these macros can be used without losing flexibility.386 387 For example ,consider a function that is polymorphic over types that have a286 arguments so these macros can be used without losing flexibility. 287 288 For example consider a function that is polymorphic over types that have a 388 289 defined arithmetic exception: 389 290 \begin{cfa} … … 402 303 Both operations follow the same set of steps. 403 304 First, a user raises an exception. 404 Second, the exception propagates up the stack , searching for a handler.305 Second, the exception propagates up the stack. 405 306 Third, if a handler is found, the exception is caught and the handler is run. 406 307 After that control continues at a raise-dependent location. 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 308 Fourth, if a handler is not found, a default handler is run and, if it returns, then control 410 309 continues after the raise. 411 310 412 The differences between the two operations include how propagation is 413 performed, where execution continues after an exception is handled 414 a nd which default handler is run.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 after an exception is caught and handled, and which default handler is run. 415 314 416 315 \subsection{Termination} 417 316 \label{s:Termination} 418 Termination handling is the familiar kind of handling 419 used in most programming 317 Termination handling is the familiar EHM and used in most programming 420 318 languages with exception handling. 421 319 It is a dynamic, non-local goto. If the raised exception is matched and … … 449 347 Then propagation starts with the search. \CFA uses a ``first match" rule so 450 348 matching is performed with the copied exception as the search key. 451 It starts from the raise site and proceeds towardsbase of the stack,349 It starts from the raise in the throwing function and proceeds towards the base of the stack, 452 350 from callee to caller. 453 351 At each stack frame, a check is made for termination handlers defined by the … … 463 361 \end{cfa} 464 362 When viewed on its own, a try statement simply executes the statements 465 in the \snake{GUARDED_BLOCK} and when those are finished,363 in the \snake{GUARDED_BLOCK}, and when those are finished, 466 364 the try statement finishes. 467 365 … … 489 387 termination exception types. 490 388 The global default termination handler performs a cancellation 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} 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. 511 392 512 393 \subsection{Resumption} 513 394 \label{s:Resumption} 514 395 515 Resumption exception handling is less familar form of exception handling, 516 but is 396 Resumption exception handling is the less familar EHM, but is 517 397 just as old~\cite{Goodenough75} and is simpler in many ways. 518 398 It is a dynamic, non-local function call. If the raised exception is … … 523 403 function once the error is corrected, and 524 404 ignorable events, such as logging where nothing needs to happen and control 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}). 405 should always continue from the raise point. 530 406 531 407 A resumption raise is started with the @throwResume@ statement: … … 533 409 throwResume EXPRESSION; 534 410 \end{cfa} 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 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 542 420 resumption does not unwind the stack nor otherwise remove values from the 543 current scope, there is no need to manage memory to keep the exception544 allocated. 545 546 Then propagation starts with the search,547 f ollowing 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.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 from 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. 550 428 \begin{cfa} 551 429 try { … … 557 435 } 558 436 \end{cfa} 559 Note that termination handlers and resumption handlers may be used together 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 560 494 in a single try statement, intermixing @catch@ and @catchResume@ freely. 561 495 Each type of handler only interacts with exceptions from the matching 562 496 kind of raise. 563 Like @catch@ clauses, @catchResume@ clauses have no effect if an exception564 is not raised.565 566 The matching rules are exactly the same as well.567 The first major difference here is that after568 @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, where571 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 not575 need to be handled at all. Putting the following default handler576 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}584 497 585 498 \subsubsection{Resumption Marking} … … 588 501 not unwind the stack. A side effect is that, when a handler is matched 589 502 and run, its try block (the guarded statements) and every try statement 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. 503 searched before it are still on the stack. There presence can lead to 504 the \emph{recursive resumption problem}. 593 505 594 506 The recursive resumption problem is any situation where a resumption handler … … 604 516 When this code is executed, the guarded @throwResume@ starts a 605 517 search and matches the handler in the @catchResume@ clause. This 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 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 609 520 same handler on the stack leading to infinite recursion. 610 521 611 While this situation is trivial and easy to avoid, much more complex cycles 612 can form with multiple handlers and different exception types. 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 613 528 To prevent all of these cases, each try statement is ``marked" from the 614 time the exception search reaches it to either when a handler completes615 handling that exceptionor when the search reaches the base529 time the exception search reaches it to either when a matching handler 530 completes or when the search reaches the base 616 531 of the stack. 617 532 While a try statement is marked, its handlers are never matched, effectively … … 622 537 \end{center} 623 538 624 There are other sets of marking rules that could be used .625 For instance, marking just the handlers that caught the exception 539 There are other sets of marking rules that could be used, 540 for instance, marking just the handlers that caught the exception, 626 541 would also prevent recursive resumption. 627 However, the rule s selected mirrorwhat 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 from542 However, the rule selected mirrors what 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 from 631 546 the stack for a termination exception, \ie those on the stack 632 547 between the handler and the raise statement. … … 665 580 // Handle a failure relating to f2 further down the stack. 666 581 \end{cfa} 667 In this example ,the file that experienced the IO error is used to decide582 In this example the file that experienced the IO error is used to decide 668 583 which handler should be run, if any at all. 669 584 … … 694 609 695 610 \subsection{Comparison with Reraising} 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. 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. 704 613 \begin{center} 705 \begin{tabular}{l r}706 \begin{cfa} 707 try { 708 do_work_may_throw();709 } catch(excep tion_t * exc ;710 can_handle(exc)) { 711 handle(exc);712 } 713 714 715 614 \begin{tabular}{l|l} 615 \begin{cfa} 616 try { 617 do_work_may_throw(); 618 } catch(excep_t * ex; can_handle(ex)) { 619 620 handle(ex); 621 622 623 624 } 716 625 \end{cfa} 717 626 & 718 627 \begin{cfa} 719 628 try { 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); 629 do_work_may_throw(); 630 } catch(excep_t * ex) { 631 if (can_handle(ex)) { 632 handle(ex); 769 633 } else { 770 634 throw; … … 772 636 } 773 637 \end{cfa} 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 % } 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. 782 748 % 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. 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} 800 753 801 754 \section{Finally Clauses} 802 755 \label{s:FinallyClauses} 803 Finally clauses are used to p erform unconditional cleanup when leaving a756 Finally clauses are used to preform unconditional clean-up when leaving a 804 757 scope and are placed at the end of a try statement after any handler clauses: 805 758 \begin{cfa} … … 813 766 The @FINALLY_BLOCK@ is executed when the try statement is removed from the 814 767 stack, including when the @GUARDED_BLOCK@ finishes, any termination handler 815 finishes or during an unwind.768 finishes, or during an unwind. 816 769 The only time the block is not executed is if the program is exited before 817 770 the stack is unwound. … … 819 772 Execution of the finally block should always finish, meaning control runs off 820 773 the end of the block. This requirement ensures control always continues as if 821 the finally clause is not present, \ie finally is for cleanup ,not changing774 the finally clause is not present, \ie finally is for cleanup not changing 822 775 control flow. 823 776 Because of this requirement, local control flow out of the finally block 824 777 is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or 825 778 @return@ that causes control to leave the finally block. Other ways to leave 826 the finally block, such as a @longjmp@or termination are much harder to check,827 and at best requir eadditional run-time overhead, and so are only779 the finally block, such as a long jump or termination are much harder to check, 780 and at best requiring additional run-time overhead, and so are only 828 781 discouraged. 829 782 830 Not all languages with unwinding have finally clauses. Notably ,\Cpp does783 Not all languages with unwinding have finally clauses. Notably \Cpp does 831 784 without it as destructors, and the RAII design pattern, serve a similar role. 832 785 Although destructors and finally clauses can be used for the same cases, 833 786 they have their own strengths, similar to top-level function and lambda 834 787 functions with closures. 835 Destructors take more work to create, but if there is clean-up code788 Destructors take more work for their creation, but if there is clean-up code 836 789 that needs to be run every time a type is used, they are much easier 837 to set up for each use. % It's automatic.838 On the other hand , finally clauses capture the local context, so areeasy to839 use when the clean up is not dependent on the type of a variable or requires790 to set-up. 791 On the other hand finally clauses capture the local context, so is easy to 792 use when the clean-up is not dependent on the type of a variable or requires 840 793 information from multiple variables. 841 794 … … 844 797 Cancellation is a stack-level abort, which can be thought of as as an 845 798 uncatchable termination. It unwinds the entire current stack, and if 846 possible ,forwards the cancellation exception to a different stack.799 possible forwards the cancellation exception to a different stack. 847 800 848 801 Cancellation is not an exception operation like termination or resumption. 849 802 There is no special statement for starting a cancellation; instead the standard 850 library function @cancel_stack@ is called ,passing an exception. Unlike a851 raise, this exception is not used in matching ,only to pass information about803 library function @cancel_stack@ is called passing an exception. Unlike a 804 raise, this exception is not used in matching only to pass information about 852 805 the cause of the cancellation. 853 Final ly, 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 memory806 Finaly, 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 memory 856 809 and the current stack is unwound. 857 810 The behaviour after that depends on the kind of stack being cancelled. 858 811 859 812 \paragraph{Main Stack} 860 The main stack is the one used by 861 the program's main function at the start of execution, 813 The main stack is the one used by the program main at the start of execution, 862 814 and is the only stack in a sequential program. 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 there866 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.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. 871 823 872 824 \paragraph{Thread Stack} … … 880 832 and an implicit join (from a destructor call). The explicit join takes the 881 833 default handler (@defaultResumptionHandler@) from its calling context while 882 the implicit join provides its own ,which does a program abort if the834 the implicit join provides its own; which does a program abort if the 883 835 @ThreadCancelled@ exception cannot be handled. 884 836 … … 898 850 899 851 With explicit join and a default handler that triggers a cancellation, it is 900 possible to cascade an error across any number of threads, 901 alternating between the resumption (possibly termination) and cancellation, 902 cleaning up each 852 possible to cascade an error across any number of threads, cleaning up each 903 853 in turn, until the error is handled or the main thread is reached. 904 854 … … 908 858 After a coroutine stack is unwound, control returns to the @resume@ function 909 859 that most recently resumed it. @resume@ reports a 910 @CoroutineCancelled@ exception, which contains a reference to the cancelled860 @CoroutineCancelled@ exception, which contains a references to the cancelled 911 861 coroutine and the exception used to cancel it. 912 862 The @resume@ function also takes the \defaultResumptionHandler{} from the 913 863 caller's context and passes it to the internal report. 914 864 915 A coroutine only knows of two other coroutines, 916 its starter and its last resumer. 865 A coroutine only knows of two other coroutines, its starter and its last resumer. 917 866 The starter has a much more distant connection, while the last resumer just 918 867 (in terms of coroutine state) called resume on this coroutine, so the message … … 920 869 921 870 With a default handler that triggers a cancellation, it is possible to 922 cascade an error across any number of coroutines, 923 alternating between the resumption (possibly termination) and cancellation, 924 cleaning up each in turn, 871 cascade an error across any number of coroutines, cleaning up each in turn, 925 872 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 you 875 talk about handling a cancellation in the last sentence. Which is correct?} -
doc/theses/andrew_beach_MMath/future.tex
rb7fd9daf rf95634e 2 2 \label{c:future} 3 3 4 The following discussion covers both possible interesting research5 that could follow from this work as well as simple implementation6 improvements.7 8 4 \section{Language Improvements} 9 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.} 10 8 \CFA is a developing programming language. As such, there are partially or 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. 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. 17 13 \begin{itemize} 14 \item 15 The implementation of termination is not portable because it includes 16 hand-crafted assembly statements. 17 The existing compilers cannot translate that for other platforms and those 18 sections must be ported by hand to 19 support more hardware architectures, such as the ARM processor. 18 20 \item 19 21 Due to a type-system problem, the catch clause cannot bind the exception to a … … 22 24 result in little or no change in the exception system but simplify usage. 23 25 \item 24 The @copy@ function in the exception virtual table is an adapter to address25 some limitations in the \CFA copy constructor. If the copy constructor is26 improved it can be used directly without the adapter.27 \item28 26 Termination handlers cannot use local control-flow transfers, \eg by @break@, 29 27 @return@, \etc. The reason is that current code generation hoists a handler 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. 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. 39 32 \item 40 There is no detection of colliding unwinds. It is possible for clean up code41 run during an unwind to trigger another unwind that escapes the clean up code42 itself ,such as a termination exception caught further down the stack or a43 cancellation. There do exist ways to handle this case, but currently there is44 no detectionand the first unwind will simply be forgotten, often leaving33 There is no detection of colliding unwinds. It is possible for clean-up code 34 run during an unwind to trigger another unwind that escapes the clean-up code 35 itself; such as a termination exception caught further down the stack or a 36 cancellation. There do exist ways to handle this but currently they are not 37 even detected and the first unwind will simply be forgotten, often leaving 45 38 it in a bad state. 46 39 \item 47 Finally, the exception system has not had a lot of programmer testing.48 More time with encouraged usage will reveal new49 quality of life upgrades that can be made .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 new 42 quality of life upgrades that can be made with time. 50 43 \end{itemize} 51 44 … … 54 47 project, but was thrust upon it to do exception inheritance; hence, only 55 48 minimal work is done. A draft for a complete virtual system is available but 56 not finalized.A future \CFA project is to complete that work and then49 it is not finalized. A future \CFA project is to complete that work and then 57 50 update the exception system that uses the current version. 58 51 … … 60 53 exception traits. The most important one is an assertion to check one virtual 61 54 type is a child of another. This check precisely captures many of the 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. 55 correctness requirements. 67 56 68 57 The full virtual system might also include other improvement like associated 69 58 types to allow traits to refer to types not listed in their header. This 70 59 feature allows exception traits to not refer to the virtual-table type 71 explicitly, removing the need for the current interface macros, 72 such as @EHM_IS_EXCEPTION@. 60 explicitly, removing the need for the current interface macros. 73 61 74 62 \section{Additional Raises} 75 63 Several other kinds of exception raises were considered beyond termination 76 (@throw@), resumption (@throwResume@), and re -raise.64 (@throw@), resumption (@throwResume@), and reraise. 77 65 78 66 The first is a non-local/concurrent raise providing asynchronous exceptions, … … 86 74 Non-local/concurrent raise requires more 87 75 coordination between the concurrency system 88 and the exception system. Many of the interesting design decisions cent er76 and the exception system. Many of the interesting design decisions centre 89 77 around masking, \ie controlling which exceptions may be thrown at a stack. It 90 78 would likely require more of the virtual system and would also effect how … … 105 93 Checked exceptions make exceptions part of a function's type by adding an 106 94 exception signature. An exception signature must declare all checked 107 exceptions that could propagate from the function ,either because they were108 raised inside the function or came from a sub-function . This improves safety95 exceptions that could propagate from the function (either because they were 96 raised inside the function or came from a sub-function). This improves safety 109 97 by making sure every checked exception is either handled or consciously 110 98 passed on. 111 99 112 Checked exceptions were never seriously considered for this project113 because they have significant trade-offs in usab ility and code reuse in100 However checked exceptions were never seriously considered for this project 101 because they have significant trade-offs in usablity and code reuse in 114 102 exchange for the increased safety. 115 103 These trade-offs are most problematic when trying to pass exceptions through 116 104 higher-order functions from the functions the user passed into the 117 105 higher-order function. There are no well known solutions to this problem 118 that were satisfactory for \CFA (which carries some of C's 119 flexibility-over-safety design) so additional research is needed.106 that were satisfactory for \CFA (which carries some of C's flexibility 107 over safety design) so additional research is needed. 120 108 121 109 Follow-up work might add some form of checked exceptions to \CFA, … … 140 128 Zero-cost resumptions is still an open problem. First, because libunwind does 141 129 not support a successful-exiting stack-search without doing an unwind. 142 Workarounds are possible but awkward. Ideally ,an extension to libunwind could143 be made, but that would either require separate maintenance or gain ingenough144 support to have it folded into the official library itself.130 Workarounds are possible but awkward. Ideally an extension to libunwind could 131 be made, but that would either require separate maintenance or gain enough 132 support to have it folded into the standard. 145 133 146 Also ,new techniques to skip previously searched parts of the stack need to be134 Also new techniques to skip previously searched parts of the stack need to be 147 135 developed to handle the recursive resume problem and support advanced algebraic 148 136 effects. … … 170 158 to leave the handler. 171 159 Currently, mimicking this behaviour in \CFA is possible by throwing a 172 termination exceptioninside a resumption handler.160 termination inside a resumption handler. 173 161 174 162 % Maybe talk about the escape; and escape CONTROL_STMT; statements or how -
doc/theses/andrew_beach_MMath/implement.tex
rb7fd9daf rf95634e 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 two public features, virtual17 cast and virtual tables,18 substantial structure is required to support them,16 While the \CFA virtual system currently has only one public feature, virtual 17 cast (see the virtual cast feature \vpageref{p:VirtualCast}), 18 substantial structure is required to support it, 19 19 and provide features for exception handling and the standard library. 20 20 21 21 \subsection{Virtual Type} 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 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 26 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 28 32 virtual table and the virtual members there. 29 It is always the first field in the 30 structure so that its location is always known. 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 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 84 40 type has no parent, the null pointer. 85 The ancestors of a virtual type are found by traversing type IDs through 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 86 45 the type information. 87 An example using helper macros looks like: 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. 62 63 Type information is constructed as follows: 64 \begin{enumerate} 65 \item 66 Use the type's name to generate a name for the type information structure. 67 This is saved so it may be reused. 68 \item 69 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, 71 but the types used change from instance to instance. 72 The generated name is used for both this structure and, if relivant, the 73 parent pointer. 74 If the virtual type is polymorphic then the type information structure is 75 polymorphic as well, with the same polymorphic arguments. 76 \item 77 A seperate name for instances is generated from the type's name. 78 \item 79 The definition is generated and initialised. 80 The parent id is set to the null pointer or to the address of the parent's 81 type information instance. Name resolution handles the rest. 82 \item 83 \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 85 including different instances of the same polymorphic type. 86 \end{enumerate} 87 \todo{The list is making me realise, some of this isn't ordered.} 88 89 Writing that code manually, with helper macros for the early name mangling, 90 would look like this: 88 91 \begin{cfa} 89 92 struct INFO_TYPE(TYPE) { … … 97 100 \end{cfa} 98 101 99 Type information is constructed as follows:100 \begin{enumerate}[nosep]101 \item102 Use the type's name to generate a name for the type information structure,103 which is saved so it can be reused.104 \item105 Generate a new structure definition to store the type106 information. The layout is the same in each case, just the parent's type ID,107 but the types used change from instance to instance.108 The generated name is used for both this structure and, if relevant, the109 parent pointer.110 If the virtual type is polymorphic then the type information structure is111 polymorphic as well, with the same polymorphic arguments.112 \item113 A separate name for instances is generated from the type's name.114 \item115 The definition is generated and initialized.116 The parent ID is set to the null pointer or to the address of the parent's117 type information instance. Name resolution handles the rest.118 \item119 \CFA's name mangler does its regular name mangling encoding the type of120 the declaration into the instance name.121 This process gives a completely unique name122 including different instances of the same polymorphic type.123 \end{enumerate}124 125 Writing that code manually, with helper macros for the early name mangling,126 would look like this:127 \begin{cfa}128 struct INFO_TYPE(TYPE) {129 INFO_TYPE(PARENT) const * parent;130 };131 132 __attribute__((cfa_linkonce))133 INFO_TYPE(TYPE) const INFO_NAME(TYPE) = {134 &INFO_NAME(PARENT),135 };136 \end{cfa}137 138 \begin{comment}139 102 \subsubsection{\lstinline{cfa\_linkonce} Attribute} 140 % I just reali zed: This is an extension of the inline keyword.103 % I just realised: This is an extension of the inline keyword. 141 104 % An extension of C's at least, it is very similar to C++'s. 142 105 Another feature added to \CFA is a new attribute: \texttt{cfa\_linkonce}. … … 150 113 file as if it was a forward declaration, except no definition is required. 151 114 152 This technique is used for type IDinstances. A link-once definition is115 This technique is used for type-id instances. A link-once definition is 153 116 generated each time the structure is seen. This will result in multiple 154 117 copies but the link-once attribute ensures all but one are removed for a … … 163 126 everything that comes after the special prefix, then only one is used 164 127 and the other is discarded. 165 \end{comment}166 128 167 129 \subsection{Virtual Table} 168 130 \label{ss:VirtualTable} 169 Each virtual type has a virtual table type that stores its type IDand131 Each virtual type has a virtual table type that stores its type id and 170 132 virtual members. 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 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 175 136 below. 176 137 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 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 179 141 there to ensure that it can be found even when the accessing code does not 180 142 know which virtual type it has. 181 The second section isall the virtual members of the parent, in the same143 The second section are all the virtual members of the parent, in the same 182 144 order as they appear in the parent's virtual table. Note that the type may 183 change slightly as references to the ``this" change. This is limited to145 change slightly as references to the ``this" will change. This is limited to 184 146 inside pointers/references and via function pointers so that the size (and 185 147 hence the offsets) are the same. … … 188 150 189 151 \begin{figure} 190 \begin{center}191 152 \input{vtable-layout} 192 \end{center}193 153 \caption{Virtual Table Layout} 194 154 \label{f:VirtualTableLayout} 155 \todo*{Improve the Virtual Table Layout diagram.} 195 156 \end{figure} 196 157 … … 199 160 This, combined with the fixed offset to the virtual table pointer, means that 200 161 for any virtual type, it is always safe to access its virtual table and, 201 from there, it is safe to check the type IDto identify the exact type of the162 from there, it is safe to check the type id to identify the exact type of the 202 163 underlying object, access any of the virtual members and pass the object to 203 164 any of the method-like virtual members. … … 207 168 the context of the declaration. 208 169 209 The type ID is always fixed,with each virtual table type having210 exactly one possible type ID.170 The type id is always fixed; with each virtual table type having 171 exactly one possible type id. 211 172 The virtual members are usually filled in by type resolution. 212 173 The best match for a given name and type at the declaration site is used. 213 174 There are two exceptions to that rule: the @size@ field, the type's size, 214 is set using a @sizeof@ expression ,and the @align@ field, the175 is set using a @sizeof@ expression and the @align@ field, the 215 176 type's alignment, is set using an @alignof@ expression. 216 177 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} 178 \subsubsection{Concurrency Integration} 245 179 Coroutines and threads need instances of @CoroutineCancelled@ and 246 180 @ThreadCancelled@ respectively to use all of their functionality. When a new … … 249 183 at the definition of the main function. 250 184 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, 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, 256 189 only new ones are added. 257 190 258 \begin{figure} [htb]191 \begin{figure} 259 192 \begin{cfa} 260 193 coroutine Example { … … 274 207 extern CoroutineCancelled_vtable & _default_vtable; 275 208 \end{cfa} 276 \caption{Co routineType Transformation}277 \label{f:Co routineTypeTransformation}209 \caption{Concurrency Type Transformation} 210 \label{f:ConcurrencyTypeTransformation} 278 211 \end{figure} 279 212 280 \begin{figure} [htb]213 \begin{figure} 281 214 \begin{cfa} 282 215 void main(Example & this) { … … 296 229 &_default_vtable_object_declaration; 297 230 \end{cfa} 298 \caption{Co routineMain Transformation}299 \label{f:Co routineMainTransformation}231 \caption{Concurrency Main Transformation} 232 \label{f:ConcurrencyMainTransformation} 300 233 \end{figure} 301 234 … … 309 242 \begin{cfa} 310 243 void * __cfa__virtual_cast( 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 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 316 248 the cast target is passed in as @child@. 317 The generated C code wraps both arguments and the result with type casts. 249 250 For generated C code wraps both arguments and the result with type casts. 318 251 There is also an internal check inside the compiler to make sure that the 319 252 target type is a virtual type. … … 322 255 The virtual cast either returns the original pointer or the null pointer 323 256 as the new type. 324 The function does the parent check and returns the appropriate value.325 The parent check is a simple linear search of thechild's ancestors using the257 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 the 326 259 type information. 327 260 328 261 \section{Exceptions} 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. 262 % Anything about exception construction. 379 263 380 264 \section{Unwinding} … … 390 274 stack. On function entry and return, unwinding is handled directly by the 391 275 call/return code embedded in the function. 392 393 % Discussing normal stack unwinding: 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 394 280 Usually, the stack-frame size is known statically based on parameter and 395 local variable declarations. Even for adynamic stack-size, the information281 local variable declarations. Even with dynamic stack-size, the information 396 282 to determine how much of the stack has to be removed is still contained 397 283 within the function. … … 399 285 bumping the hardware stack-pointer up or down as needed. 400 286 Constructing/destructing values within a stack frame has 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 287 a similar complexity but can add additional work and take longer. 288 289 Unwinding across multiple stack frames is more complex because that 405 290 information is no longer contained within the current function. 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 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 415 298 @longjmp@. This approach bypasses the need to know stack details by simply 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 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 427 308 (conceptually) popped from the stack. 428 None of these cases should be handled by the user-- that would contradict the429 intention of these features -- so they need to be handled automatically.309 None of these should be handled by the user --- that would contradict the 310 intention of these features --- so they need to be handled automatically. 430 311 431 312 To safely remove sections of the stack, the language must be able to find and 432 run these clean up actions even when removing multiple functions unknown at313 run these clean-up actions even when removing multiple functions unknown at 433 314 the beginning of the unwinding. 434 315 … … 436 317 library that provides tools for stack walking, handler execution, and 437 318 unwinding. What follows is an overview of all the relevant features of 438 libunwind needed for this work. 439 Following that is the description of the \CFA code that uses libunwind 440 to implement termination. 319 libunwind needed for this work, and how \CFA uses them to implement exception 320 handling. 441 321 442 322 \subsection{libunwind Usage} … … 468 348 In plain C (which \CFA currently compiles down to) this 469 349 flag only handles the cleanup attribute: 470 %\label{code:cleanup}471 350 \begin{cfa} 472 351 void clean_up( int * var ) { ... } … … 476 355 in this case @clean_up@, run when the variable goes out of scope. 477 356 This feature is enough to mimic destructors, 478 but not try statements that affect357 but not try statements which can effect 479 358 the unwinding. 480 359 481 360 To get full unwinding support, all of these features must be handled directly 482 in assembly and assembler directives; parti cularly the cfi directives361 in assembly and assembler directives; partiularly the cfi directives 483 362 \snake{.cfi_lsda} and \snake{.cfi_personality}. 484 363 … … 520 399 @_UA_FORCE_UNWIND@ specifies a forced unwind call. Forced unwind only performs 521 400 the cleanup phase and uses a different means to decide when to stop 522 (see \ autoref{s:ForcedUnwind}).401 (see \vref{s:ForcedUnwind}). 523 402 \end{enumerate} 524 403 525 404 The @exception_class@ argument is a copy of the 526 405 \code{C}{exception}'s @exception_class@ field, 527 which is a number that identifies the EHM406 which is a number that identifies the exception handling mechanism 528 407 that created the exception. 529 408 … … 531 410 provided storage object. It has two public fields: the @exception_class@, 532 411 which is described above, and the @exception_cleanup@ function. 533 The clean up function is used by the EHM to clean up the exception. If it412 The clean-up function is used by the EHM to clean-up the exception, if it 534 413 should need to be freed at an unusual time, it takes an argument that says 535 414 why it had to be cleaned up. … … 553 432 of the most recent stack frame. It continues to call personality functions 554 433 traversing the stack from newest to oldest until a function finds a handler or 555 the end of the stack is reached. In the latter case, 556 @_U nwind_RaiseException@ returns @_URC_END_OF_STACK@.557 558 Second, when a handler is matched, @_Unwind_RaiseException@559 moves to the cleanupphase and walks the stack a second time.434 the end of the stack is reached. In the latter case, raise exception returns 435 @_URC_END_OF_STACK@. 436 437 Second, when a handler is matched, raise exception moves to the clean-up 438 phase and walks the stack a second time. 560 439 Once again, it calls the personality functions of each stack frame from newest 561 440 to oldest. This pass stops at the stack frame containing the matching handler. 562 If that personality function has not install eda handler, it is an error.563 564 If an error is encountered, @_Unwind_RaiseException@returns either441 If that personality function has not install a handler, it is an error. 442 443 If an error is encountered, raise exception returns either 565 444 @_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the 566 445 error occurred. … … 573 452 _Unwind_Stop_Fn, void *); 574 453 \end{cfa} 575 It also unwinds the stack but it does not use the search phase. Instead, 576 another 454 It also unwinds the stack but it does not use the search phase. Instead another 577 455 function, the stop function, is used to stop searching. The exception is the 578 same as the one passed to @_Unwind_RaiseException@. 579 The extra arguments are the stop 456 same as the one passed to raise exception. The extra arguments are the stop 580 457 function and the stop parameter. The stop function has a similar interface as a 581 458 personality function, except it is also passed the stop parameter. … … 617 494 needs its own exception context. 618 495 619 The currentexception context should be retrieved by calling the function496 The exception context should be retrieved by calling the function 620 497 \snake{this_exception_context}. 621 498 For sequential execution, this function is defined as … … 642 519 The first step of a termination raise is to copy the exception into memory 643 520 managed by the exception system. Currently, the system uses @malloc@, rather 644 than reserved memory or the stack top. The EHMmanages521 than reserved memory or the stack top. The exception handling mechanism manages 645 522 memory for the exception as well as memory for libunwind and the system's own 646 523 per-exception storage. … … 677 554 \newsavebox{\stackBox} 678 555 \begin{lrbox}{\codeBox} 679 \begin{ cfa}556 \begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}] 680 557 unsigned num_exceptions = 0; 681 558 void throws() { … … 696 573 throws(); 697 574 } 698 \end{ cfa}575 \end{lstlisting} 699 576 \end{lrbox} 700 577 701 578 \begin{lrbox}{\stackBox} 702 579 \begin{lstlisting} 703 | finally block (Example)704 | try block580 | try-finally 581 | try-catch (Example) 705 582 throws() 706 | finally block (Example)707 | try block583 | try-finally 584 | try-catch (Example) 708 585 throws() 709 | finally block (Example)710 | try block586 | try-finally 587 | try-catch (Example) 711 588 throws() 712 589 main() … … 721 598 \label{f:MultipleExceptions} 722 599 \end{figure} 600 \todo*{Work on multiple exceptions code sample.} 723 601 724 602 All exceptions are stored in nodes, which are then linked together in lists 725 603 one list per stack, with the 726 604 list head stored in the exception context. Within each linked list, the most 727 recently thrown exception is at the head ,followed by older thrown605 recently thrown exception is at the head followed by older thrown 728 606 exceptions. This format allows exceptions to be thrown, while a different 729 607 exception is being handled. The exception at the head of the list is currently … … 736 614 exception into managed memory. After the exception is handled, the free 737 615 function is used to clean up the exception and then the entire node is 738 passed to @free@, returning the memory back to the heap.616 passed to free, returning the memory back to the heap. 739 617 740 618 \subsection{Try Statements and Catch Clauses} 741 619 The try statement with termination handlers is complex because it must 742 compensate for the C code-generation versus proper620 compensate for the C code-generation versus 743 621 assembly-code generated from \CFA. Libunwind 744 622 requires an LSDA and personality function for control to unwind across a 745 623 function. The LSDA in particular is hard to mimic in generated C code. 746 624 747 The workaround is a function called \snake{__cfaehm_try_terminate} in the748 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.625 The workaround is a function called @__cfaehm_try_terminate@ in the standard 626 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. 751 629 Because this function is known and fixed (and not an arbitrary function that 752 happens to contain a try statement), itsLSDA can be generated ahead630 happens to contain a try statement), the LSDA can be generated ahead 753 631 of time. 754 632 755 Both the LSDA and the personality function for \snake{__cfaehm_try_terminate} 756 are set ahead of time using 633 Both the LSDA and the personality function are set ahead of time using 757 634 embedded assembly. This assembly code is handcrafted using C @asm@ statements 758 635 and contains 759 enough information for the single try statement the function represents.636 enough information for a single try statement the function repersents. 760 637 761 638 The three functions passed to try terminate are: 762 639 \begin{description} 763 \item[try function:] This function is the try block . It is where all the code640 \item[try function:] This function is the try block, it is where all the code 764 641 from inside the try block is placed. It takes no parameters and has no 765 642 return value. This function is called during regular execution to run the try … … 769 646 decides if a catch clause matches the termination exception. It is constructed 770 647 from the conditional part of each handler and runs each check, top to bottom, 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 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 778 651 handler that matches the exception. 779 652 … … 786 659 \end{description} 787 660 All three functions are created with GCC nested functions. GCC nested functions 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 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 793 664 variables in scope for the function containing the @try@ statement. These 794 665 nested functions and all other functions besides @__cfaehm_try_terminate@ in … … 798 669 799 670 \autoref{f:TerminationTransformation} shows the pattern used to transform 800 a \CFA try statement with catch clauses into the appropriate C functions. 671 a \CFA try statement with catch clauses into the approprate C functions. 672 \todo{Explain the Termination Transformation figure.} 801 673 802 674 \begin{figure} … … 856 728 \caption{Termination Transformation} 857 729 \label{f:TerminationTransformation} 730 \todo*{Improve (compress?) Termination Transformations.} 858 731 \end{figure} 859 732 … … 865 738 Instead of storing the data in a special area using assembly, 866 739 there is just a linked list of possible handlers for each stack, 867 with each node on the list rep resenting a try statement on the stack.740 with each node on the list reperenting a try statement on the stack. 868 741 869 742 The head of the list is stored in the exception context. … … 871 744 to the head of the list. 872 745 Instead of traversing the stack, resumption handling traverses the list. 873 At each node, the EHM checks to see if the try statement the node rep resents746 At each node, the EHM checks to see if the try statement the node repersents 874 747 can handle the exception. If it can, then the exception is handled and 875 the operation finishes ; otherwise,the search continues to the next node.748 the operation finishes, otherwise the search continues to the next node. 876 749 If the search reaches the end of the list without finding a try statement 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. 750 that can handle the exception, the default handler is executed and the 751 operation finishes. 880 752 881 753 Each node has a handler function that does most of the work. 882 754 The handler function is passed the raised exception and returns true 883 755 if the exception is handled and false otherwise. 756 884 757 The handler function checks each of its internal handlers in order, 885 top-to-bottom, until it f inds a match. If a match is found that handler is758 top-to-bottom, until it funds a match. If a match is found that handler is 886 759 run, after which the function returns true, ignoring all remaining handlers. 887 760 If no match is found the function returns false. 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. 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. 895 765 896 766 \autoref{f:ResumptionTransformation} shows the pattern used to transform 897 a \CFA try statement with catch Resume clauses into the appropriate898 C functions. 767 a \CFA try statement with catch clauses into the approprate C functions. 768 \todo{Explain the Resumption Transformation figure.} 899 769 900 770 \begin{figure} … … 937 807 \caption{Resumption Transformation} 938 808 \label{f:ResumptionTransformation} 809 \todo*{Improve (compress?) Resumption Transformations.} 939 810 \end{figure} 940 811 941 812 % Recursive Resumption Stuff: 942 813 \autoref{f:ResumptionMarking} shows search skipping 943 (see \ autoref{s:ResumptionMarking}), which ignores parts of814 (see \vpageref{s:ResumptionMarking}), which ignores parts of 944 815 the stack 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 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 948 818 is updated to the next node of the current node. After the search is complete, 949 819 successful or not, the head of the list is reset. … … 952 822 been checked are not on the list while a handler is run. If a resumption is 953 823 thrown during the handling of another resumption, the active handlers and all 954 the other handler schecked up to this point are not checked again.824 the other handler checked up to this point are not checked again. 955 825 % No paragraph? 956 826 This structure also supports new handlers added while the resumption is being 957 827 handled. These are added to the front of the list, pointing back along the 958 stack -- the first one points over all the checked handlers--828 stack --- the first one points over all the checked handlers --- 959 829 and the ordering is maintained. 960 830 961 831 \begin{figure} 962 \centering963 832 \input{resumption-marking} 964 833 \caption{Resumption Marking} 965 834 \label{f:ResumptionMarking} 835 \todo*{Label Resumption Marking to aid clarity.} 966 836 \end{figure} 967 837 … … 981 851 \section{Finally} 982 852 % Uses destructors and GCC nested functions. 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 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 1026 861 up, which runs the function that contains the finally code. 1027 862 … … 1029 864 % Stack selections, the three internal unwind functions. 1030 865 1031 Cancellation also uses libunwind to do its stack traversal and unwinding .1032 However,it uses a different primary function: @_Unwind_ForcedUnwind@. Details1033 of its interface can be found in Section~\vref{s:ForcedUnwind}.866 Cancellation also uses libunwind to do its stack traversal and unwinding, 867 however it uses a different primary function: @_Unwind_ForcedUnwind@. Details 868 of its interface can be found in the Section~\vref{s:ForcedUnwind}. 1034 869 1035 870 The first step of cancellation is to find the cancelled stack and its type: … … 1052 887 passed to the forced-unwind function. The general pattern of all three stop 1053 888 functions is the same: continue unwinding until the end of stack and 1054 then p erform the appropriate transfer.889 then preform the appropriate transfer. 1055 890 1056 891 For main stack cancellation, the transfer is just a program abort. -
doc/theses/andrew_beach_MMath/intro.tex
rb7fd9daf rf95634e 11 11 12 12 % Now take a step back and explain what exceptions are generally. 13 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). 13 17 Exception handling provides dynamic inter-function control flow. 14 A language's EHM is a combination of language syntax and run-time15 components that construct, raise, propagate and handle exceptions,16 to provide all of that control flow.17 18 There are two forms of exception handling covered in this thesis: 18 19 termination, which acts as a multi-level return, 19 20 and resumption, which is a dynamic function call. 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. 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. 40 30 \begin{center} 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. 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} 47 52 \end{center} 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.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. 55 60 \begin{center} 56 %\input{resumption} 57 % 58 %\medskip 59 \input{resumhandle.pstex_t} 60 % The other one. 61 \input{termination} 61 62 \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 previous 66 function on the stack, skipping any functions between it and the 67 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. 62 75 63 76 Although a powerful feature, exception handling tends to be complex to set up 64 and expensive to use ,77 and expensive to use 65 78 so it is often limited to unusual or ``exceptional" cases. 66 The classic example is error handling ; exceptions can be used to67 remove error handling logic from the main execution path, and pay79 The classic example is error handling, where exceptions are used to 80 remove error handling logic from the main execution path, while paying 68 81 most of the cost only when the error actually occurs. 69 82 … … 72 85 The \CFA EHM implements all of the common exception features (or an 73 86 equivalent) found in most other EHMs and adds some features of its own. 74 The design of all the features had to be adapted to \CFA's feature set ,as87 The design of all the features had to be adapted to \CFA's feature set as 75 88 some of the underlying tools used to implement and express exception handling 76 89 in other languages are absent in \CFA. 77 Still , the resultingsyntax resembles that of other languages:78 \begin{ cfa}79 try{90 Still the resulting basic syntax resembles that of other languages: 91 \begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}] 92 @try@ { 80 93 ... 81 94 T * object = malloc(request_size); 82 95 if (!object) { 83 throwOutOfMemory{fixed_allocation, request_size};96 @throw@ OutOfMemory{fixed_allocation, request_size}; 84 97 } 85 98 ... 86 } catch(OutOfMemory * error) {99 } @catch@ (OutOfMemory * error) { 87 100 ... 88 101 } 89 \end{ cfa}102 \end{lstlisting} 90 103 % A note that yes, that was a very fast overview. 91 104 The design and implementation of all of \CFA's EHM's features are … … 94 107 95 108 % The current state of the project and what it contributes. 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 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 101 113 languages and much of the design is as well. 102 Some parts of the EHM use other features unique to \CFA and would be 103 harder to replicate in other programming languages. 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. 104 120 105 121 The contributions of this work are: 106 122 \begin{enumerate} 107 123 \item Designing \CFA's exception handling mechanism, adapting designs from 108 other programming languages and creating new features.109 \item Implementing stack unwinding andthe \CFA EHM, including updating110 the \CFA compiler and the run-time environment.124 other programming languages, and creating new features. 125 \item Implementing stack unwinding for the \CFA EHM, including updating 126 the \CFA compiler and run-time environment to generate and execute the EHM code. 111 127 \item Designing and implementing a prototype virtual system. 112 128 % I think the virtual system and per-call site default handlers are the only 113 129 % "new" features, everything else is a matter of implementation. 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. 130 \item Creating tests and performance benchmarks to compare with EHM's in other languages. 117 131 \end{enumerate} 118 132 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},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 \CFA EHM features are introduced in \autoref{c:features}, 123 137 covering their usage and design. 124 138 That is followed by the implementation of these features in 125 139 \autoref{c:implement}. 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}. 140 Performance results are presented in \autoref{c:performance}. 141 Summing up and possibilities for extending this project are discussed in \autoref{c:future}. 129 142 130 143 \section{Background} 131 144 \label{s:background} 132 145 133 Exception handling has been examined beforein programming languages,134 with papers on the subject dating back 70s.\cite{Goodenough75}146 Exception handling is a well examined area in programming languages, 147 with papers on the subject dating back the 70s~\cite{Goodenough75}. 135 148 Early exceptions were often treated as signals, which carried no information 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, 149 except their identity. Ada~\cite{Ada} still uses this system. 150 151 The modern flag-ship for termination exceptions is \Cpp, 141 152 which added them in its first major wave of non-object-orientated features 142 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 from153 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 of inheriting from 147 158 \code{C++}{std::exception}. 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 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 153 163 exceptions have this functionality. 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} 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} 161 170 and it uses checked exceptions. 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). 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. 186 181 187 182 %\subsection 188 183 Resumption exceptions are less popular, 189 although resumption is as old as termination; that is, few 184 although resumption is as old as termination; 185 hence, few 190 186 programming languages have implemented them. 191 187 % http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/parc/techReports/ 192 188 % CSL-79-3_Mesa_Language_Manual_Version_5.0.pdf 193 Mesa is one programming language that did.\cite{Mesa}Experience with Mesa194 is quoted as being one of the reasons resumptions were not189 Mesa~\cite{Mesa} is one programming languages that did. Experience with Mesa 190 is quoted as being one of the reasons resumptions are not 195 191 included in the \Cpp standard. 196 192 % https://en.wikipedia.org/wiki/Exception_handling 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. 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. 206 201 % Especially considering how much easier they are to implement than 207 % termination exceptions and how much Peter likes them.208 209 %\subsection 210 Functional languages tend to use other solutions for their primary error211 handling mechanism,but exception-like constructs still appear.212 Termination appears in theerror construct, which marks the result of an213 expression as an error; the n the result of any expression that tries to use214 it also results in anerror, and so on until an appropriate handler is reached.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 an 208 expression as an error; thereafter, the result of any expression that tries to use it is also an 209 error, and so on until an appropriate handler is reached. 215 210 Resumption appears in algebraic effects, where a function dispatches its 216 211 side-effects to its caller for handling. 217 212 218 213 %\subsection 219 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 by222 unwinding the stack like in termination exception 223 handling.\cite{RustPanicMacro}\cite{RustPanicModule} 224 Go's panic thoughis very similar to a termination, except it only supports214 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 by 217 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 panic is very similar to a termination, except it only supports 225 220 a catch-all by calling \code{Go}{recover()}, simplifying the interface at 226 the cost of flexibility. \cite{Go:2021}227 228 %\subsection 229 Asexception handling's most common use cases are in error handling,230 here are some other ways to handle errors with comparisons withexceptions.221 the cost of flexibility. 222 223 %\subsection 224 While exception handling's most common use cases are in error handling, 225 here are other ways to handle errors with comparisons to exceptions. 231 226 \begin{itemize} 232 227 \item\emph{Error Codes}: 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, 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, 240 234 which leads to an error being quietly and implicitly ignored. 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} 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.. 264 238 265 239 \item\emph{Special Return with Global Store}: 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? 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. 297 248 298 249 \item\emph{Return Union}: … … 300 251 Success is one tag and the errors are another. 301 252 It is also possible to make each possible error its own tag and carry its own 302 additional information, but the two -branch format is easy to make generic253 additional information, but the two branch format is easy to make generic 303 254 so that one type can be used everywhere in error handling code. 304 255 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. 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. 317 259 % Rust's \code{rust}{Result<T, E>} 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} 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. 343 265 344 266 \item\emph{Handler Functions}: 345 This pattern associates errors with functions.346 On error, the function that produced the error calls another function to267 This pattern implicitly associates functions with errors. 268 On error, the function that produced the error implicitly calls another function to 347 269 handle it. 348 270 The handler function can be provided locally (passed in as an argument, 349 271 either directly as as a field of a structure/object) or globally (a global 350 272 variable). 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} 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. 378 281 \end{itemize} 379 282 380 283 %\subsection 381 284 Because of their cost, exceptions are rarely used for hot paths of execution. 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} 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 -
doc/theses/andrew_beach_MMath/performance.tex
rb7fd9daf rf95634e 3 3 4 4 Performance is of secondary importance for most of this project. 5 Instead, the focus was to get the features working. The only performance5 Instead, the focus is 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. Hence, only a few basic performance tests were performed to 8 check this requirement. 7 amount of time. 9 8 10 9 \section{Test Set-Up} 11 10 Tests were run in \CFA, C++, Java and Python. 12 11 In addition there are two sets of tests for \CFA, 13 one with termination and one with resumption.14 15 GCCC++ is the most comparable language because both it and \CFA use the same12 one for termination and one for resumption exceptions. 13 14 C++ is the most comparable language because both it and \CFA use the same 16 15 framework, libunwind. 17 In fact, the comparison is almost entirely in quality of implementation.18 Specifically,\CFA's EHM has had significantly less time to be optimized and16 In fact, the comparison is almost entirely a quality of implementation 17 comparison: \CFA's EHM has had significantly less time to be optimized and 19 18 does not generate its own assembly. It does have a slight advantage in that 20 \Cpp has to do some extra bookkeeping to support itsutility functions,21 but otherwise \Cpp should havea significant advantage.22 23 Java , a popular language with similar termination semantics,24 is implemented in a very different environment, a virtual machine with19 there are some features it handles directly instead of through utility functions, 20 but otherwise \Cpp has a significant advantage. 21 22 Java is another very popular language with similar termination semantics. 23 It is implemented in a very different environment, a virtual machine with 25 24 garbage collection. 26 It also implements the finally clause on tryblocks allowing for a direct25 It also implements the @finally@ clause on @try@ blocks allowing for a direct 27 26 feature-to-feature comparison. 28 As with \Cpp, Java's implementation is mature, has moreoptimizations29 and extra features as compared to \CFA.30 31 Python is used as an alternative comparison because of the \CFA EHM's32 current performance goals, which is to notbe prohibitively slow while the27 As with \Cpp, Java's implementation is mature, optimizations 28 and has extra features. 29 30 Python is used as an alternative point of comparison because of the \CFA EHM's 31 current performance goals, which is not to be prohibitively slow while the 33 32 features are designed and examined. Python has similar performance goals for 34 33 creating quick scripts and its wide use suggests it has achieved those goals. … … 37 36 resumption exceptions. Even the older programming languages with resumption 38 37 seem to be notable only for having resumption. 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. 38 So instead, resumption is compared to a less similar but much more familiar 39 feature, termination exceptions. 46 40 47 41 All tests are run inside a main loop that repeatedly performs a test. 48 42 This approach avoids start-up or tear-down time from 49 43 affecting the timing results. 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. 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. 55 47 56 48 Timing is done internally, with time measured immediately before and … … 59 51 unhandled exceptions in \Cpp and Java as that would cause the process to 60 52 terminate. 61 Luckily, performance on the ``give up and kill the process" path is not53 Luckily, performance on the ``give-up and kill the process" path is not 62 54 critical. 63 55 64 56 The exceptions used in these tests are always based off of 65 the base exception for the language. 66 This requirement minimizes performance differences based 57 a base exception. This requirement minimizes performance differences based 67 58 on the object model used to represent the exception. 68 59 … … 71 62 For example, empty inline assembly blocks are used in \CFA and \Cpp to 72 63 prevent excessive optimizations while adding no actual work. 64 Each test was run eleven times. The top three and bottom three results were 65 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 is 68 compiled with 11.0.11. Python with 3.8. The tests were run on: 69 \begin{itemize}[nosep] 70 \item 71 ARM 2280 Kunpeng 920 48-core 2$\times$socket \lstinline{@} 2.6 GHz running Linux v5.11.0-25 72 \item 73 AMD 6380 Abu Dhabi 16-core 4$\times$socket \lstinline{@} 2.5 GHz running Linux v5.11.0-25 74 \end{itemize} 73 75 74 76 % We don't use catch-alls but if we did: 75 77 % Catch-alls are done by catching the root exception type (not using \Cpp's 76 78 % \code{C++}{catch(...)}). 77 78 When collecting data, each test is run eleven times. The top three and bottom79 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$socket88 \lstinline{@} 2.6 GHz running Linux v5.11.0-2589 \item AMD 6380 Abu Dhabi 16-core 4$\times$socket90 \lstinline{@} 2.5 GHz running Linux v5.11.0-2591 \end{itemize}92 These represent the two major families of hardware architecture.93 79 94 80 \section{Tests} … … 97 83 They should provide a guide as to where the EHM's costs are found. 98 84 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: 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 109 89 \begin{cfa} 110 90 void unwind_empty(unsigned int frames) { 111 91 if (frames) { 112 92 unwind_empty(frames - 1); 113 } else { 114 throwResume (empty_exception){&empty_vt}; 115 } 93 } else throw (empty_exception){&empty_vt}; 116 94 } 117 95 \end{cfa} 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. 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. 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. 98 \begin{itemize}[nosep] 127 99 \item Empty: 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. 100 This test measures the cost of raising (stack walking) an exception through empty 101 empty stack frames to an empty handler. (see above) 132 102 \item Destructor: 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. 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} 137 112 \item Finally: 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. 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} 142 122 \item Other Handler: 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. 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} 149 133 \end{itemize} 150 134 151 135 \paragraph{Cross Try Statement} 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. 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: 139 \begin{cfa} 140 try { 141 } catch (not_raised_exception *) {} 142 \end{cfa} 157 143 \begin{itemize}[nosep] 158 144 \item Handler: 159 The try statement has a handler (of the appropriate kind).145 The try statement has a handler. 160 146 \item Finally: 161 The try statement hasa finally clause.147 The try statement replaces the handler with a finally clause. 162 148 \end{itemize} 163 149 164 150 \paragraph{Conditional Matching} 165 This group measures the cost of conditional matching.151 This final group measures the cost of conditional matching. 166 152 Only \CFA implements the language level conditional match, 167 the other languages m imic itwith an ``unconditional" match (it still168 checks the exception's type) and conditional re-raise if it is not supposed153 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 169 155 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} 156 \begin{center} 157 \begin{tabular}{ll} 158 \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp, Java, Python} \\ 175 159 \begin{cfa} 176 160 try { 177 ... 178 } catch (exception_t * e ; 179 should_catch(e)) { 180 ... 161 throw_exception(); 162 } catch (empty_exception * exc; 163 should_catch) { 181 164 } 182 165 \end{cfa} 183 \end{minipage} 184 \begin{minipage}{0.55\textwidth} 185 \begin{lstlisting}[language=C++] 166 & 167 \begin{cfa} 186 168 try { 187 ... 188 } catch (std::exception & e) { 189 if (!should_catch(e)) throw; 190 ... 169 throw_exception(); 170 } catch (EmptyException & exc) { 171 if (!should_catch) throw; 191 172 } 192 \end{lstlisting} 193 \end{minipage} 173 \end{cfa} 174 \end{tabular} 175 \end{center} 194 176 \begin{itemize}[nosep] 195 177 \item Match All: … … 199 181 \end{itemize} 200 182 201 \paragraph{Resumption Simulation}202 A slightly altered version of the Empty Traversal test is used when comparing203 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 a207 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.209 210 183 %\section{Cost in Size} 211 184 %Using exceptions also has a cost in the size of the executable. … … 219 192 220 193 \section{Results} 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 \\ 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 \\ 270 249 \hline 271 250 \end{tabular} 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 \\ 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 \\ 290 299 \hline 291 300 \end{tabular} 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. 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. -
doc/theses/andrew_beach_MMath/resumption-marking.fig
rb7fd9daf rf95634e 8 8 -2 9 9 1200 2 10 6 5985 1530 6165 3105 11 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 1620 90 90 6075 1620 6075 1710 12 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 2340 90 90 6075 2340 6075 2430 13 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 3015 90 90 6075 3015 6075 3105 14 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 15 1 1 1.00 60.00 120.00 16 6075 1755 6075 2205 17 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 18 1 1 1.00 60.00 120.00 19 6075 2475 6075 2925 20 -6 21 6 3465 1530 3645 3105 22 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 1620 90 90 3555 1620 3555 1710 23 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 2340 90 90 3555 2340 3555 2430 24 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 3015 90 90 3555 3015 3555 3105 25 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 26 1 1 1.00 60.00 120.00 27 3555 1755 3555 2205 28 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 29 1 1 1.00 60.00 120.00 30 3555 2475 3555 2925 31 -6 32 6 2115 1530 2295 3105 33 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 1620 90 90 2205 1620 2205 1710 34 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 2340 90 90 2205 2340 2205 2430 35 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 3015 90 90 2205 3015 2205 3105 36 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 37 1 1 1.00 60.00 120.00 38 2205 1755 2205 2205 39 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 40 1 1 1.00 60.00 120.00 41 2205 2475 2205 2925 42 -6 10 43 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 3105 11 45 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 945 90 90 4905 945 4905 1035 12 46 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 2340 90 90 4905 2340 4905 2430 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 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 23 59 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 24 60 1 1 1.00 60.00 120.00 … … 30 66 1 1 1.00 60.00 120.00 31 67 4770 1080 4590 1260 4590 2070 4770 2250 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 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 -
doc/theses/andrew_beach_MMath/uw-ethesis-frontpgs.tex
rb7fd9daf rf95634e 129 129 \begin{center}\textbf{Abstract}\end{center} 130 130 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. 131 This is the abstract. 150 132 151 133 \cleardoublepage … … 156 138 \begin{center}\textbf{Acknowledgements}\end{center} 157 139 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 140 I would like to thank all the little people who made this thesis possible. 183 141 \cleardoublepage 184 142 -
doc/theses/andrew_beach_MMath/uw-ethesis.bib
rb7fd9daf rf95634e 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.4 3 5 @misc{Dice21, 6 author = {Dave Dice}, 7 year = 2021, 8 month = aug, 9 howpublished= {personal communication} 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" 10 11 } 11 12 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}}, 13 @book{knuth.book, 14 author = "Donald Knuth", 15 title = "The \TeX book", 16 year = "1986", 17 publisher = "Addison-Wesley", 18 address = "Reading, Massachusetts" 17 19 } 18 20 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}, 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" 25 28 } 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
rb7fd9daf rf95634e 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}]{@}{@}} 212 214 % Annotations from Peter: 213 215 \newcommand{\PAB}[1]{{\color{blue}PAB: #1}} -
doc/theses/andrew_beach_MMath/vtable-layout.fig
rb7fd9daf rf95634e 8 8 -2 9 9 1200 2 10 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1 11 1620 1665 10 12 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 11 13 3510 1890 3645 1755 … … 14 16 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 15 17 3645 1305 3645 1755 16 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 217 2115 1935 2250 193518 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 419 2250 1170 2115 1170 2115 2475 2250 247520 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 221 2250 1350 2115 135022 18 4 0 0 50 -1 0 12 0.0000 4 165 630 2295 1305 type_id\001 23 19 4 0 0 50 -1 0 12 0.0000 4 165 1170 2295 1500 parent_field0\001 -
doc/theses/mubeen_zulfiqar_MMath/allocator.tex
rb7fd9daf rf95634e 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 29 28 \begin{itemize} 30 29 \item … … 118 117 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. 119 118 120 \subsubsection void * aalloc( size \_t dim, size\_t elemSize )119 \subsubsection void * aalloc( size_t dim, size_t elemSize ) 121 120 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. 122 121 \paragraph{Usage} 123 122 aalloc takes two parameters. 124 125 123 \begin{itemize} 126 124 \item … … 131 129 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. 132 130 133 \subsubsection void * resize( void * oaddr, size \_t size )131 \subsubsection void * resize( void * oaddr, size_t size ) 134 132 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. 135 133 \paragraph{Usage} 136 134 resize takes two parameters. 137 138 135 \begin{itemize} 139 136 \item … … 144 141 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. 145 142 146 \subsubsection void * resize( void * oaddr, size \_t nalign, size\_t size )143 \subsubsection void * resize( void * oaddr, size_t nalign, size_t size ) 147 144 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. 148 145 \paragraph{Usage} 149 146 This resize takes three parameters. It takes an additional parameter of nalign as compared to the above resize (FIX ME: cite above resize). 150 151 147 \begin{itemize} 152 148 \item … … 159 155 It returns an object with the size and alignment given in the parameters. On failure, it returns a NULL pointer. 160 156 161 \subsubsection void * amemalign( size \_t alignment, size\_t dim, size\_t elemSize )157 \subsubsection void * amemalign( size_t alignment, size_t dim, size_t elemSize ) 162 158 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. 163 159 \paragraph{Usage} 164 160 amemalign takes three parameters. 165 166 161 \begin{itemize} 167 162 \item … … 174 169 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. 175 170 176 \subsubsection void * cmemalign( size \_t alignment, size\_t dim, size\_t elemSize )171 \subsubsection void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) 177 172 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. 178 173 \paragraph{Usage} 179 174 cmemalign takes three parameters. 180 181 175 \begin{itemize} 182 176 \item … … 189 183 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. 190 184 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 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. 196 189 \begin{itemize} 197 190 \item 198 191 addr: the address of the currently allocated dynamic object. 199 192 \end{itemize} 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 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. 207 199 \begin{itemize} 208 200 \item 209 201 addr: the address of the currently allocated dynamic object. 210 202 \end{itemize} 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 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. 218 209 \begin{itemize} 219 210 \item 220 211 addr: the address of the currently allocated dynamic object. 221 212 \end{itemize} 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 )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 ) 225 216 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. 226 217 \paragraph{Usage} 227 218 This realloc takes three parameters. It takes an additional parameter of nalign as compared to the default realloc. 228 229 219 \begin{itemize} 230 220 \item … … 247 237 It returns a dynamic object of the size of type T. On failure, it return NULL pointer. 248 238 249 \subsubsection T * aalloc( size \_t dim )239 \subsubsection T * aalloc( size_t dim ) 250 240 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. 251 241 \paragraph{Usage} 252 242 aalloc takes one parameters. 253 254 243 \begin{itemize} 255 244 \item … … 258 247 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. 259 248 260 \subsubsection T * calloc( size \_t dim )249 \subsubsection T * calloc( size_t dim ) 261 250 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. 262 251 \paragraph{Usage} 263 252 This calloc takes one parameter. 264 265 253 \begin{itemize} 266 254 \item … … 269 257 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. 270 258 271 \subsubsection T * resize( T * ptr, size \_t size )259 \subsubsection T * resize( T * ptr, size_t size ) 272 260 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. 273 261 \paragraph{Usage} 274 262 This resize takes two parameters. 275 276 263 \begin{itemize} 277 264 \item … … 282 269 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. 283 270 284 \subsubsection T * realloc( T * ptr, size \_t size )271 \subsubsection T * realloc( T * ptr, size_t size ) 285 272 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. 286 273 \paragraph{Usage} 287 274 This realloc takes two parameters. 288 289 275 \begin{itemize} 290 276 \item … … 295 281 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. 296 282 297 \subsubsection T * memalign( size \_t align )283 \subsubsection T * memalign( size_t align ) 298 284 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. 299 285 \paragraph{Usage} 300 286 memalign takes one parameters. 301 302 287 \begin{itemize} 303 288 \item … … 306 291 It returns a dynamic object of the size of type T that is aligned to given parameter align. On failure, it return NULL pointer. 307 292 308 \subsubsection T * amemalign( size \_t align, size\_t dim )293 \subsubsection T * amemalign( size_t align, size_t dim ) 309 294 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. 310 295 \paragraph{Usage} 311 296 amemalign takes two parameters. 312 313 297 \begin{itemize} 314 298 \item … … 319 303 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. 320 304 321 \subsubsection T * cmemalign( size \_t align, size\_t dim )305 \subsubsection T * cmemalign( size_t align, size_t dim ) 322 306 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. 323 307 \paragraph{Usage} 324 308 cmemalign takes two parameters. 325 326 309 \begin{itemize} 327 310 \item … … 332 315 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. 333 316 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 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. 339 321 \begin{itemize} 340 322 \item … … 343 325 It returns a dynamic object of the size of type T that is aligned to the given parameter. On failure, it return NULL pointer. 344 326 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 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. 350 331 \begin{itemize} 351 332 \item … … 354 335 align: required alignment of the dynamic object. 355 336 \end{itemize} 356 357 337 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. 358 338 … … 369 349 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. 370 350 371 \subsection Alloc Interface351 \subsection{Alloc Interface} 372 352 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. 373 353 This interface helps programmers in three major ways. 374 375 354 \begin{itemize} 376 355 \item … … 392 371 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. 393 372 It represents the required number of members in the array allocation as in CFA's aalloc (FIX ME: cite aalloc). 394 This parameter should be of type size \_t.373 This parameter should be of type size_t. 395 374 396 375 Example: int a = alloc( 5 ) … … 398 377 399 378 \paragraph{Align} 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.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. 401 380 402 381 Example: int b = alloc( 5 , 64`align ) … … 406 385 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. 407 386 Three types of parameters can be passed using `fill. 408 409 387 \begin{itemize} 410 388 \item -
doc/theses/mubeen_zulfiqar_MMath/background.tex
rb7fd9daf rf95634e 23 23 ==================== 24 24 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. 25 \cite{Wasik08} -
doc/theses/mubeen_zulfiqar_MMath/benchmarks.tex
rb7fd9daf rf95634e 149 149 *** FIX ME: Insert a figure of above benchmark with description 150 150 151 \paragr aph{Relevant Knobs}151 \paragrpah{Relevant Knobs} 152 152 *** FIX ME: Insert Relevant Knobs 153 153 -
doc/theses/mubeen_zulfiqar_MMath/intro.tex
rb7fd9daf rf95634e 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. 63 101 64 102 \section{Research Objectives} -
doc/theses/mubeen_zulfiqar_MMath/performance.tex
rb7fd9daf rf95634e 44 44 tc & & \\ 45 45 \end{tabularx} 46 47 %(FIX ME: complete table) 46 (FIX ME: complete table) 48 47 49 48 \section{Experiment Environment} -
doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.bib
rb7fd9daf rf95634e 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
rb7fd9daf rf95634e 84 84 \usepackage{graphicx} 85 85 \usepackage{comment} % Removes large sections of the document. 86 \usepackage{tabularx}87 86 88 87 % Hyperlinks make it very easy to navigate an electronic document. … … 192 191 % Tip: Putting each sentence on a new line is a way to simplify later editing. 193 192 %---------------------------------------------------------------------- 194 \begin{sloppypar}195 196 193 \input{intro} 197 194 \input{background} … … 200 197 \input{performance} 201 198 \input{conclusion} 202 203 \end{sloppypar}204 199 205 200 %---------------------------------------------------------------------- -
doc/theses/thierry_delisle_PhD/.gitignore
rb7fd9daf rf95634e 13 13 comp_II/presentation.pdf 14 14 15 seminars/build/16 seminars/img/*.fig.bak17 seminars/*.pdf18 19 15 thesis/build/ 20 16 thesis/fig/*.fig.bak -
doc/theses/thierry_delisle_PhD/thesis/Makefile
rb7fd9daf rf95634e 20 20 practice \ 21 21 io \ 22 eval_micro \23 eval_macro \24 22 }} 25 23 … … 37 35 pivot_ring \ 38 36 system \ 39 cycle \40 37 } 41 38 -
doc/theses/thierry_delisle_PhD/thesis/thesis.tex
rb7fd9daf rf95634e 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 \ input{text/eval_micro.tex}239 \ input{text/eval_macro.tex}237 \chapter{Theoretical and Existance Proofs} 238 \chapter{Micro-Benchmarks} 239 \chapter{Larger-Scale applications} 240 240 \part{Conclusion \& Annexes} 241 241 -
doc/user/user.tex
rb7fd9daf rf95634e 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Sun Oct 10 12:45:00202114 %% Update Count : 50 9513 %% Last Modified On : Mon May 31 09:03:34 2021 14 %% Update Count : 5071 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 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 ";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 "; // manipulator 4449 $\emph{thread\(_2\)}$ : sout | ®acquire® | "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, he long form of the mutex statement is used, \eg: 4469 \begin{cfa} 4470 ®mutex( sout )® { 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}$ 4471 4472 sout | 1; 4472 ®mutex() sout® | 2 | 3; $\C{// unnecessary, but ok because of recursive lock}$4473 sout | ®acquire® | 2 | 3; $\C{// unnecessary, but ok to acquire and release again}$ 4473 4474 sout | 4; 4474 } // implicitly release sout lock4475 \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.4475 } // implicitly release the lock when "acq" is deallocated 4476 \end{cfa} 4477 Note, the unnecessary ©acquire© manipulator works because the recursive stream-lock can be acquired/released multiple times by the owner thread. 4477 4478 Hence, calls to functions that also acquire a stream lock for their output do not result in \Index{deadlock}. 4478 4479 4479 4480 The previous values written by threads 1 and 2 can be read in concurrently: 4480 4481 \begin{cfa} 4481 ®mutex( sin )® { 4482 { // acquire sin lock for block duration 4483 ®isacquire acq = { sin };® $\C{// named stream locker}$ 4482 4484 int x, y, z, w; 4483 4485 sin | x; 4484 ®mutex() sin® | y | z; $\C{// unnecessary, but ok because of recursive lock}$4486 sin | ®acquire® | y | z; $\C{// unnecessary, but ok to acquire and release again}$ 4485 4487 sin | w; 4486 } // implicitly release sin lock4488 } // implicitly release the lock when "acq" is deallocated 4487 4489 \end{cfa} 4488 4490 Again, the order of the reading threads is non-deterministic. … … 4491 4493 \Textbf{WARNING:} The general problem of \Index{nested locking} can occur if routines are called in an I/O sequence that block, \eg: 4492 4494 \begin{cfa} 4493 ®mutex() sout® | "data:" | rtn( mon ); $\C{// mutex call on monitor}$4495 sout | ®acquire® | "data:" | rtn( mon ); $\C{// mutex call on monitor}$ 4494 4496 \end{cfa} 4495 4497 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. … … 4498 4500 \begin{cfa} 4499 4501 int ®data® = rtn( mon ); 4500 mutex() sout| "data:" | ®data®;4502 sout | acquire | "data:" | ®data®; 4501 4503 \end{cfa} 4502 4504 … … 4504 4506 \section{String Stream} 4505 4507 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 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. 4524 4510 \begin{figure} 4525 4511 \begin{cfa} … … 4534 4520 double x = 12345678.9, y = 98765.4321e-11; 4535 4521 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}$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}$ 4542 4528 ®istrstream isstr = { buf2 };® 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 4529 isstr | i | j | k | x | y; 4530 sout | i | j | k | x | y; 4531 } 4552 4532 \end{cfa} 4553 4533 \caption{String Stream Processing} 4554 4534 \label{f:StringStreamProcessing} 4555 4535 \end{figure} 4536 4537 \VRef[Figure]{f:StringStreamFunctions} shows the string stream operations. 4538 \begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt] 4539 \item 4540 \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 \item 4545 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 4556 4564 4557 4565 \begin{comment} -
libcfa/prelude/bootloader.cf
rb7fd9daf rf95634e 3 3 char ** cfa_args_argv; 4 4 char ** cfa_args_envp; 5 __attribute__((weak)) extern int cfa_main_returned;5 int cfa_main_returned = 0; 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 if(&cfa_main_returned)cfa_main_returned = 1;12 cfa_main_returned = 1; 13 13 return ret; 14 14 } -
libcfa/prelude/builtins.c
rb7fd9daf rf95634e 10 10 // Created On : Fri Jul 21 16:21:03 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Aug 14 08:45:54 202113 // Update Count : 1 3312 // Last Modified On : Wed Jul 21 13:31:34 2021 13 // Update Count : 129 14 14 // 15 15 … … 107 107 #endif // __SIZEOF_INT128__ 108 108 109 // for-control index constraints110 // 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 113 109 // exponentiation operator implementation 114 110 -
libcfa/src/Makefile.am
rb7fd9daf rf95634e 48 48 math.hfa \ 49 49 time_t.hfa \ 50 bits/algorithm.hfa \51 50 bits/align.hfa \ 52 51 bits/containers.hfa \ … … 78 77 memory.hfa \ 79 78 parseargs.hfa \ 80 parseconfig.hfa \81 79 rational.hfa \ 82 80 stdlib.hfa \ … … 87 85 containers/pair.hfa \ 88 86 containers/result.hfa \ 89 containers/string.hfa \90 containers/string_res.hfa \91 87 containers/vector.hfa \ 92 88 device/cpu.hfa … … 94 90 libsrc = ${inst_headers_src} ${inst_headers_src:.hfa=.cfa} \ 95 91 assert.cfa \ 92 bits/algorithm.hfa \ 96 93 bits/debug.cfa \ 97 94 exception.c \ … … 109 106 concurrency/invoke.h \ 110 107 concurrency/future.hfa \ 111 concurrency/kernel/fwd.hfa \ 112 concurrency/mutex_stmt.hfa 108 concurrency/kernel/fwd.hfa 113 109 114 110 inst_thread_headers_src = \ … … 196 192 $(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@} 197 193 198 concurrency/io/call.cfa: $(srcdir)/concurrency/io/call.cfa.in199 ${AM_V_GEN}python3 $< > $@200 201 194 #---------------------------------------------------------------------------------------------------------------- 202 195 libcfa_la_SOURCES = ${libsrc} -
libcfa/src/concurrency/clib/cfathread.cfa
rb7fd9daf rf95634e 13 13 // Update Count : 14 14 // 15 16 // #define EPOLL_FOR_SOCKETS17 15 18 16 #include "fstream.hfa" … … 25 23 #include "cfathread.h" 26 24 27 extern "C" {28 #include <string.h>29 #include <errno.h>30 }31 32 25 extern void ?{}(processor &, const char[], cluster &, thread$ *); 33 26 extern "C" { 34 27 extern void __cfactx_invoke_thread(void (*main)(void *), void * this); 35 extern int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);36 28 } 37 29 38 30 extern Time __kernel_get_time(); 39 extern unsigned register_proc_id( void );40 31 41 32 //================================================================================ 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 33 // Thread run y the C Interface 225 34 226 35 struct cfathread_object { … … 436 245 // Mutex 437 246 struct cfathread_mutex { 438 linear_backoff_then_block_lock impl;247 fast_lock impl; 439 248 }; 440 249 int cfathread_mutex_init(cfathread_mutex_t *restrict mut, const cfathread_mutexattr_t *restrict) __attribute__((nonnull (1))) { *mut = new(); return 0; } … … 451 260 // Condition 452 261 struct cfathread_condition { 453 condition_variable( linear_backoff_then_block_lock) impl;262 condition_variable(fast_lock) impl; 454 263 }; 455 264 int cfathread_cond_init(cfathread_cond_t *restrict cond, const cfathread_condattr_t *restrict) __attribute__((nonnull (1))) { *cond = new(); return 0; } … … 479 288 // IO operations 480 289 int cfathread_socket(int domain, int type, int protocol) { 481 return socket(domain, type 482 #if defined(EPOLL_FOR_SOCKETS) 483 | SOCK_NONBLOCK 484 #endif 485 , protocol); 290 return socket(domain, type, protocol); 486 291 } 487 292 int cfathread_bind(int socket, const struct sockaddr *address, socklen_t address_len) { … … 494 299 495 300 int cfathread_accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len) { 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 301 return cfa_accept4(socket, address, address_len, 0, CFA_IO_LAZY); 510 302 } 511 303 512 304 int cfathread_connect(int socket, const struct sockaddr *address, socklen_t address_len) { 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 305 return cfa_connect(socket, address, address_len, CFA_IO_LAZY); 526 306 } 527 307 … … 535 315 536 316 ssize_t cfathread_sendmsg(int socket, const struct msghdr *message, int flags) { 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; 317 return cfa_sendmsg(socket, message, flags, CFA_IO_LAZY); 552 318 } 553 319 554 320 ssize_t cfathread_write(int fildes, const void *buf, size_t nbyte) { 555 321 // Use send rather then write for socket since it's faster 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; 322 return cfa_send(fildes, buf, nbyte, 0, CFA_IO_LAZY); 571 323 } 572 324 … … 584 336 msg.msg_controllen = 0; 585 337 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 338 ssize_t ret = cfa_recvmsg(socket, &msg, flags, CFA_IO_LAZY); 599 339 600 340 if(address_len) *address_len = msg.msg_namelen; … … 604 344 ssize_t cfathread_read(int fildes, void *buf, size_t nbyte) { 605 345 // Use recv rather then read for socket since it's faster 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 } 346 return cfa_recv(fildes, buf, nbyte, 0, CFA_IO_LAZY); 347 } 348 349 } -
libcfa/src/concurrency/invoke.h
rb7fd9daf rf95634e 170 170 bool corctx_flag; 171 171 172 int last_cpu; 173 172 174 //SKULLDUGGERY errno is not save in the thread data structure because returnToKernel appears to be the only function to require saving and restoring it 173 175 … … 175 177 struct cluster * curr_cluster; 176 178 177 // preferred ready-queue or CPU179 // preferred ready-queue 178 180 unsigned preferred; 179 181 -
libcfa/src/concurrency/io.cfa
rb7fd9daf rf95634e 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 , unpark_hint);92 extern void __kernel_unpark( thread$ * thrd ); 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 ) , UNPARK_LOCAL);120 __kernel_unpark( fulfil( *future, cqe.res, false ) ); 121 121 } 122 122 … … 183 183 ctx.proc->io.pending = false; 184 184 185 ready_schedule_lock(); 185 186 __cfa_io_drain( proc ); 187 ready_schedule_unlock(); 186 188 // for(i; 2) { 187 189 // unsigned idx = proc->rdq.id + i; … … 309 311 // Make the sqes visible to the submitter 310 312 __atomic_store_n(sq.kring.tail, tail + have, __ATOMIC_RELEASE); 311 sq.to_submit += have;313 sq.to_submit++; 312 314 313 315 ctx->proc->io.pending = true; 314 316 ctx->proc->io.dirty = true; 315 317 if(sq.to_submit > 30 || !lazy) { 316 ready_schedule_lock();317 318 __cfa_io_flush( ctx->proc ); 318 ready_schedule_unlock();319 319 } 320 320 } -
libcfa/src/concurrency/io/types.hfa
rb7fd9daf rf95634e 188 188 return wait(this.self); 189 189 } 190 191 void reset( io_future_t & this ) {192 return reset(this.self);193 }194 190 } -
libcfa/src/concurrency/kernel.cfa
rb7fd9daf rf95634e 22 22 #include <errno.h> 23 23 #include <stdio.h> 24 #include <string.h>25 24 #include <signal.h> 26 25 #include <unistd.h> … … 32 31 #include "kernel_private.hfa" 33 32 #include "preemption.hfa" 34 #include "strstream.hfa"35 #include "device/cpu.hfa"36 33 37 34 //Private includes … … 196 193 197 194 if( !readyThread ) { 198 ready_schedule_lock();199 195 __cfa_io_flush( this ); 200 ready_schedule_unlock();201 202 196 readyThread = __next_thread_slow( this->cltr ); 203 197 } … … 237 231 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle); 238 232 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 } 233 __disable_interrupts_hard(); 234 eventfd_t val; 235 eventfd_read( this->idle, &val ); 236 __enable_interrupts_hard(); 256 237 257 238 #if !defined(__CFA_NO_STATISTICS__) … … 280 261 281 262 if(this->io.pending && !this->io.dirty) { 282 ready_schedule_lock();283 263 __cfa_io_flush( this ); 284 ready_schedule_unlock();285 264 } 286 265 … … 322 301 323 302 // Don't block if we are done 324 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) { 325 ready_schedule_unlock(); 326 break MAIN_LOOP; 327 } 303 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 328 304 329 305 __STATS( __tls_stats()->ready.sleep.halts++; ) … … 349 325 } 350 326 351 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )327 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); ) 352 328 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle); 353 329 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 } 330 // __disable_interrupts_hard(); 331 eventfd_t val; 332 eventfd_read( this->idle, &val ); 333 // __enable_interrupts_hard(); 371 334 372 335 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); ) … … 430 393 /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next ); 431 394 __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; 432 405 433 406 __cfadbg_print_safe(runtime_core, "Kernel : core %p running thread %p (%s)\n", this, thrd_dst, thrd_dst->self_cor.name); … … 484 457 if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) { 485 458 // The thread was preempted, reschedule it and reset the flag 486 schedule_thread$( thrd_dst , UNPARK_LOCAL);459 schedule_thread$( thrd_dst ); 487 460 break RUNNING; 488 461 } … … 568 541 // Scheduler routines 569 542 // KERNEL ONLY 570 static void __schedule_thread( thread$ * thrd , unpark_hint hint) {543 static void __schedule_thread( thread$ * thrd ) { 571 544 /* paranoid */ verify( ! __preemption_enabled() ); 572 545 /* paranoid */ verify( ready_schedule_islocked()); … … 588 561 // Dereference the thread now because once we push it, there is not guaranteed it's still valid. 589 562 struct cluster * cl = thrd->curr_cluster; 590 __STATS(bool outside = hint == UNPARK_LOCAL &&thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )563 __STATS(bool outside = thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; ) 591 564 592 565 // push the thread to the cluster ready-queue 593 push( cl, thrd, hint);566 push( cl, thrd, local ); 594 567 595 568 // variable thrd is no longer safe to use … … 616 589 } 617 590 618 void schedule_thread$( thread$ * thrd , unpark_hint hint) {591 void schedule_thread$( thread$ * thrd ) { 619 592 ready_schedule_lock(); 620 __schedule_thread( thrd , hint);593 __schedule_thread( thrd ); 621 594 ready_schedule_unlock(); 622 595 } … … 669 642 } 670 643 671 void __kernel_unpark( thread$ * thrd , unpark_hint hint) {644 void __kernel_unpark( thread$ * thrd ) { 672 645 /* paranoid */ verify( ! __preemption_enabled() ); 673 646 /* paranoid */ verify( ready_schedule_islocked()); … … 677 650 if(__must_unpark(thrd)) { 678 651 // Wake lost the race, 679 __schedule_thread( thrd , hint);652 __schedule_thread( thrd ); 680 653 } 681 654 … … 684 657 } 685 658 686 void unpark( thread$ * thrd , unpark_hint hint) {659 void unpark( thread$ * thrd ) { 687 660 if( !thrd ) return; 688 661 … … 690 663 disable_interrupts(); 691 664 // Wake lost the race, 692 schedule_thread$( thrd , hint);665 schedule_thread$( thrd ); 693 666 enable_interrupts(false); 694 667 } … … 947 920 /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count); 948 921 /* paranoid */ verify( it->local_data->this_stats ); 949 // __print_stats( it->local_data->this_stats, cltr->print_stats, "Processor", it->name, (void*)it );950 922 __tally_stats( cltr->stats, it->local_data->this_stats ); 951 923 it = &(*it)`next; … … 957 929 // this doesn't solve all problems but does solve many 958 930 // so it's probably good enough 959 disable_interrupts();960 931 uint_fast32_t last_size = ready_mutate_lock(); 961 932 … … 965 936 // Unlock the RWlock 966 937 ready_mutate_unlock( last_size ); 967 enable_interrupts();968 938 } 969 939 -
libcfa/src/concurrency/kernel.hfa
rb7fd9daf rf95634e 151 151 struct __attribute__((aligned(128))) __timestamp_t { 152 152 volatile unsigned long long tv; 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; } 153 }; 154 155 static inline void ?{}(__timestamp_t & this) { this.tv = 0; } 164 156 static inline void ^?{}(__timestamp_t & this) {} 165 157 … … 177 169 // Array of times 178 170 __timestamp_t * volatile tscs; 179 180 // Array of stats181 __help_cnts_t * volatile help;182 171 183 172 // Number of lanes (empty or not) -
libcfa/src/concurrency/kernel/fwd.hfa
rb7fd9daf rf95634e 119 119 120 120 extern "Cforall" { 121 enum unpark_hint { UNPARK_LOCAL, UNPARK_REMOTE };122 123 121 extern void park( void ); 124 extern void unpark( struct thread$ *, unpark_hint ); 125 static inline void unpark( struct thread$ * thrd ) { unpark(thrd, UNPARK_LOCAL); } 122 extern void unpark( struct thread$ * this ); 126 123 static inline struct thread$ * active_thread () { 127 124 struct thread$ * t = publicTLS_get( this_thread ); -
libcfa/src/concurrency/kernel/startup.cfa
rb7fd9daf rf95634e 100 100 // Other Forward Declarations 101 101 extern void __wake_proc(processor *); 102 extern int cfa_main_returned; // from interpose.cfa103 102 104 103 //----------------------------------------------------------------------------- … … 201 200 __cfadbg_print_safe(runtime_core, "Kernel : Main cluster ready\n"); 202 201 203 // Construct the processor context of the main processor204 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 ctx221 // (the coroutine that contains the processing control flow)222 mainProcessor = (processor *)&storage_mainProcessor;223 (*mainProcessor){};224 225 register_tls( mainProcessor );226 227 202 // Start by initializing the main thread 228 203 // SKULLDUGGERY: the mainThread steals the process main thread … … 235 210 __cfadbg_print_safe(runtime_core, "Kernel : Main thread ready\n"); 236 211 212 213 214 // Construct the processor context of the main processor 215 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 ctx 232 // (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 237 239 //initialize the global state variables 238 240 __cfaabi_tls.this_processor = mainProcessor; … … 250 252 // Add the main thread to the ready queue 251 253 // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread 252 schedule_thread$(mainThread , UNPARK_LOCAL);254 schedule_thread$(mainThread); 253 255 254 256 // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX … … 269 271 270 272 static void __kernel_shutdown(void) { 271 if(!cfa_main_returned) return;272 273 /* paranoid */ verify( __preemption_enabled() ); 273 274 disable_interrupts(); … … 485 486 link.next = 0p; 486 487 link.ts = -1llu; 487 preferred = ready_queue_new_preferred();488 preferred = -1u; 488 489 last_proc = 0p; 489 490 #if defined( __CFA_WITH_VERIFY__ ) -
libcfa/src/concurrency/kernel_private.hfa
rb7fd9daf rf95634e 46 46 } 47 47 48 void schedule_thread$( thread$ * , unpark_hint hint) __attribute__((nonnull (1)));48 void schedule_thread$( thread$ * ) __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, unpark_hint hint);302 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool local); 303 303 304 304 //----------------------------------------------------------------------- … … 321 321 322 322 //----------------------------------------------------------------------- 323 // get preferred ready for new thread324 unsigned ready_queue_new_preferred();325 326 //-----------------------------------------------------------------------327 323 // Increase the width of the ready queue (number of lanes) by 4 328 324 void ready_queue_grow (struct cluster * cltr); -
libcfa/src/concurrency/locks.hfa
rb7fd9daf rf95634e 324 324 } 325 325 326 // linear backoff bounded by spin_count 327 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 times 338 yield_counter++; 339 yield(); 340 } else { break; } 341 } 342 343 // block until signalled 344 while (block(this)) if(try_lock_contention(this)) return true; 345 346 // this should never be reached as block(this) always returns true 347 return false; 348 } 349 350 static inline bool lock_improved(linear_backoff_then_block_lock & this) with(this) { 351 // if owner just return 352 if (active_thread() == owner) return true; 353 size_t compare_val = 0; 354 int spin = spin_start; 355 // linear backoff 356 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_count 366 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 times 379 yield_counter++; 380 yield(); 381 } else { break; } 382 } 383 326 384 if(2 != compare_val && try_lock_contention(this)) return true; 327 385 // block until signalled … … 344 402 static inline void on_notify(linear_backoff_then_block_lock & this, struct thread$ * t ) { unpark(t); } 345 403 static inline size_t on_wait(linear_backoff_then_block_lock & this) { unlock(this); return 0; } 346 static inline void on_wakeup(linear_backoff_then_block_lock & this, size_t recursion ) { lock (this); }404 static inline void on_wakeup(linear_backoff_then_block_lock & this, size_t recursion ) { lock_improved(this); } 347 405 348 406 //----------------------------------------------------------------------------- -
libcfa/src/concurrency/monitor.cfa
rb7fd9daf rf95634e 990 990 } 991 991 992 //-----------------------------------------------------------------------------993 // Enter routine for mutex stmt994 // Can't be accepted since a mutex stmt is effectively an anonymous routine995 // Thus we do not need a monitor group996 void lock( monitor$ * this ) {997 thread$ * thrd = active_thread();998 999 // Lock the monitor spinlock1000 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 it1009 __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 it1015 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 it1023 /* 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 leave1042 unlock( this->lock );1043 return;1044 }1045 1046 // Leave routine for mutex stmt1047 // Is just a wrapper around __leave for the is_lock trait to see1048 void unlock( monitor$ * this ) { __leave( this ); }1049 1050 992 // Local Variables: // 1051 993 // mode: c // -
libcfa/src/concurrency/monitor.hfa
rb7fd9daf rf95634e 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 null71 size_t dim = malloc_size( arr ) / sizeof( T );72 for ( int i = dim - 1; i >= 0; i -= 1 ) { // reverse allocation order, must be unsigned73 ^(arr[i]){}; // run destructor74 } // for75 free( arr );76 } // if77 } // adelete78 67 79 68 //----------------------------------------------------------------------------- … … 160 149 void __waitfor_internal( const __waitfor_mask_t & mask, int duration ); 161 150 162 // lock and unlock routines for mutex statements to use163 void lock( monitor$ * this );164 void unlock( monitor$ * this );165 166 151 // Local Variables: // 167 152 // mode: c // -
libcfa/src/concurrency/ready_queue.cfa
rb7fd9daf rf95634e 100 100 #define __kernel_rseq_unregister rseq_unregister_current_thread 101 101 #elif defined(CFA_HAVE_LINUX_RSEQ_H) 102 staticvoid __kernel_raw_rseq_register (void);103 staticvoid __kernel_raw_rseq_unregister(void);102 void __kernel_raw_rseq_register (void); 103 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 255 248 void ?{}(__ready_queue_t & this) with (this) { 256 249 #if defined(USE_CPU_WORK_STEALING) … … 258 251 lanes.data = alloc( lanes.count ); 259 252 lanes.tscs = alloc( lanes.count ); 260 lanes.help = alloc( cpu_info.hthrd_count );261 253 262 254 for( idx; (size_t)lanes.count ) { 263 255 (lanes.data[idx]){}; 264 256 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;271 257 } 272 258 #else 273 259 lanes.data = 0p; 274 260 lanes.tscs = 0p; 275 lanes.help = 0p;276 261 lanes.count = 0; 277 262 #endif … … 285 270 free(lanes.data); 286 271 free(lanes.tscs); 287 free(lanes.help);288 272 } 289 273 290 274 //----------------------------------------------------------------------- 291 275 #if defined(USE_CPU_WORK_STEALING) 292 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {276 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) { 293 277 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 294 278 295 279 processor * const proc = kernelTLS().this_processor; 296 const bool external = (!proc) || (cltr != proc->cltr); 297 298 // Figure out the current cpu and make sure it is valid 280 const bool external = !push_local || (!proc) || (cltr != proc->cltr); 281 299 282 const int cpu = __kernel_getcpu(); 300 283 /* paranoid */ verify(cpu >= 0); … … 302 285 /* paranoid */ verify(cpu * READYQ_SHARD_FACTOR < lanes.count); 303 286 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 } 287 const cpu_map_entry_t & map = cpu_info.llc_map[cpu]; 315 288 /* paranoid */ verify(map.start * READYQ_SHARD_FACTOR < lanes.count); 316 289 /* paranoid */ verify(map.self * READYQ_SHARD_FACTOR < lanes.count); … … 323 296 if(unlikely(external)) { r = __tls_rand(); } 324 297 else { r = proc->rdq.its++; } 325 choose(hint) { 326 case UNPARK_LOCAL : i = start + (r % READYQ_SHARD_FACTOR); 327 case UNPARK_REMOTE: i = prf + (r % READYQ_SHARD_FACTOR); 328 } 298 i = start + (r % READYQ_SHARD_FACTOR); 329 299 // If we can't lock it retry 330 300 } while( !__atomic_try_acquire( &lanes.data[i].lock ) ); … … 362 332 processor * const proc = kernelTLS().this_processor; 363 333 const int start = map.self * READYQ_SHARD_FACTOR; 364 const unsigned long long ctsc = rdtscl();365 334 366 335 // Did we already have a help target 367 336 if(proc->rdq.target == -1u) { 368 unsigned long long max = 0; 337 // if We don't have a 338 unsigned long long min = ts(lanes.data[start]); 369 339 for(i; READYQ_SHARD_FACTOR) { 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; 340 unsigned long long tsc = ts(lanes.data[start + i]); 341 if(tsc < min) min = tsc; 342 } 343 proc->rdq.cutoff = min; 344 374 345 /* paranoid */ verify(lanes.count < 65536); // The following code assumes max 65536 cores. 375 346 /* paranoid */ verify(map.count < 65536); // The following code assumes max 65536 cores. 376 347 377 if(0 == (__tls_rand() % 10 0)) {348 if(0 == (__tls_rand() % 10_000)) { 378 349 proc->rdq.target = __tls_rand() % lanes.count; 379 350 } else { … … 387 358 } 388 359 else { 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; 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; 395 362 { 396 363 unsigned target = proc->rdq.target; 397 364 proc->rdq.target = -1u; 398 lanes.help[target / READYQ_SHARD_FACTOR].tri++; 399 if(moving_average(ctsc - lanes.tscs[target].tv, lanes.tscs[target].ma) > cutoff) { 365 if(lanes.tscs[target].tv < cutoff && ts(lanes.data[target]) < cutoff) { 400 366 thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help)); 401 367 proc->rdq.last = target; 402 368 if(t) return t; 403 else proc->rdq.target = -1u;404 369 } 405 else proc->rdq.target = -1u;406 370 } 407 371 … … 464 428 } 465 429 466 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {430 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) { 467 431 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 468 432 469 const bool external = (hint != UNPARK_LOCAL)|| (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);433 const bool external = !push_local || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 470 434 /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count ); 471 435 … … 551 515 #endif 552 516 #if defined(USE_WORK_STEALING) 553 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {517 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) { 554 518 __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr); 555 519 556 520 // #define USE_PREFERRED 557 521 #if !defined(USE_PREFERRED) 558 const bool external = (hint != UNPARK_LOCAL)|| (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);522 const bool external = !push_local || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr); 559 523 /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count ); 560 524 #else 561 525 unsigned preferred = thrd->preferred; 562 const bool external = (hint != UNPARK_LOCAL)|| (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr;526 const bool external = push_local || (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr; 563 527 /* paranoid */ verifyf(external || preferred < lanes.count, "Invalid preferred queue %u for %u lanes", preferred, lanes.count ); 564 528 … … 681 645 // Actually pop the list 682 646 struct thread$ * thrd; 683 unsigned long long tsc_before = ts(lane);684 647 unsigned long long tsv; 685 648 [thrd, tsv] = pop(lane); … … 695 658 __STATS( stats.success++; ) 696 659 697 #if defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING) 698 unsigned long long now = rdtscl(); 660 #if defined(USE_WORK_STEALING) 699 661 lanes.tscs[w].tv = tsv; 700 lanes.tscs[w].ma = moving_average(now > tsc_before ? now - tsc_before : 0, lanes.tscs[w].ma);701 662 #endif 702 663 703 #if defined(USE_CPU_WORK_STEALING) 704 thrd->preferred = w / READYQ_SHARD_FACTOR; 705 #else 706 thrd->preferred = w; 707 #endif 664 thrd->preferred = w; 708 665 709 666 // return the popped thread … … 731 688 732 689 //----------------------------------------------------------------------- 733 // get preferred ready for new thread734 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 #endif743 }744 745 #if defined(USE_CPU_WORK_STEALING)746 /* paranoid */ verify(pref >= 0);747 /* paranoid */ verify(pref < cpu_info.hthrd_count);748 #endif749 750 return pref;751 }752 753 //-----------------------------------------------------------------------754 690 // Check that all the intrusive queues in the data structure are still consistent 755 691 static void check( __ready_queue_t & q ) with (q) { … … 979 915 extern void __enable_interrupts_hard(); 980 916 981 staticvoid __kernel_raw_rseq_register (void) {917 void __kernel_raw_rseq_register (void) { 982 918 /* paranoid */ verify( __cfaabi_rseq.cpu_id == RSEQ_CPU_ID_UNINITIALIZED ); 983 919 … … 997 933 } 998 934 999 staticvoid __kernel_raw_rseq_unregister(void) {935 void __kernel_raw_rseq_unregister(void) { 1000 936 /* paranoid */ verify( __cfaabi_rseq.cpu_id >= 0 ); 1001 937 -
libcfa/src/concurrency/ready_subqueue.hfa
rb7fd9daf rf95634e 98 98 99 99 // Get the relevant nodes locally 100 unsigned long long ts = this.anchor.ts; 100 101 thread$ * node = this.anchor.next; 101 102 this.anchor.next = node->link.next; … … 115 116 /* paranoid */ verify( node->link.ts != 0 ); 116 117 /* paranoid */ verify( this.anchor.ts != 0 ); 117 return [node, t his.anchor.ts];118 return [node, ts]; 118 119 } 119 120 -
libcfa/src/concurrency/stats.cfa
rb7fd9daf rf95634e 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;54 50 #endif 55 51 … … 108 104 tally_one( &cltr->io.calls.completed , &proc->io.calls.completed ); 109 105 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 );114 106 #endif 115 107 } … … 187 179 | " - cmp " | eng3(io.calls.drain) | "/" | eng3(io.calls.completed) | "(" | ws(3, 3, avgcomp) | "/drain)" 188 180 | " - " | 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);192 181 sstr | nl; 193 182 } -
libcfa/src/concurrency/stats.hfa
rb7fd9daf rf95634e 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;110 104 }; 111 105 #endif -
libcfa/src/concurrency/thread.cfa
rb7fd9daf rf95634e 25 25 #include "invoke.h" 26 26 27 uint64_t thread_rand();28 29 27 //----------------------------------------------------------------------------- 30 28 // Thread ctors and dtors … … 36 34 preempted = __NO_PREEMPTION; 37 35 corctx_flag = false; 36 disable_interrupts(); 37 last_cpu = __kernel_getcpu(); 38 enable_interrupts(); 38 39 curr_cor = &self_cor; 39 40 self_mon.owner = &this; … … 43 44 link.next = 0p; 44 45 link.ts = -1llu; 45 preferred = ready_queue_new_preferred();46 preferred = -1u; 46 47 last_proc = 0p; 47 48 #if defined( __CFA_WITH_VERIFY__ ) … … 140 141 /* paranoid */ verify( this_thrd->context.SP ); 141 142 142 schedule_thread$( this_thrd , UNPARK_LOCAL);143 schedule_thread$( this_thrd ); 143 144 enable_interrupts(); 144 145 } -
libcfa/src/device/cpu.cfa
rb7fd9daf rf95634e 144 144 // Count number of cpus in the system 145 145 static int count_cpus(void) { 146 const char * fpath = "/sys/devices/system/cpu/ online";146 const char * fpath = "/sys/devices/system/cpu/present"; 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
rb7fd9daf rf95634e 30 30 }; 31 31 32 externcpu_info_t cpu_info;32 cpu_info_t cpu_info; -
libcfa/src/fstream.cfa
rb7fd9daf rf95634e 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Oct 10 11:23:05202113 // Update Count : 51212 // Last Modified On : Thu Jul 29 22:34:10 2021 13 // Update Count : 454 14 14 // 15 15 … … 28 28 #define IO_MSG "I/O error: " 29 29 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;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; 38 38 sepSetCur$( os, sepGet( os ) ); 39 39 sepSet( os, " " ); … … 41 41 } // ?{} 42 42 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$ ); } 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; } 56 54 57 55 // public 58 56 void ?{}( ofstream & os ) { os.file$ = 0p; } 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 ); } 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 } // ^?{} 62 69 63 70 void sepOn( ofstream & os ) { os.sepOnOff$ = ! getNL$( os ); } … … 100 107 if ( &os == &exit ) exit( EXIT_FAILURE ); 101 108 if ( &os == &abort ) abort(); 109 if ( os.acquired$ ) { os.acquired$ = false; release( os ); } 102 110 } // ends 103 111 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$) ); } 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 107 123 108 124 void open( ofstream & os, const char name[], const char mode[] ) { 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 125 FILE * file = fopen( name, mode ); 126 #ifdef __CFA_DEBUG__ 116 127 if ( file == 0p ) { 117 128 throw (Open_Failure){ os }; 118 129 // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno ); 119 130 } // if 120 (os){ file }; // initialize 131 #endif // __CFA_DEBUG__ 132 (os){ file }; 121 133 } // open 122 134 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 ) { 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 ) { 137 144 throw (Close_Failure){ os }; 138 145 // abort | IO_MSG "close output" | nl | strerror( errno ); 139 146 } // if 140 file$ = 0p; // safety after close147 os.file$ = 0p; 141 148 } // close 142 149 … … 157 164 va_list args; 158 165 va_start( args, format ); 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 166 int len = vfprintf( (FILE *)(os.file$), format, args ); 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 } // acquire 184 185 inline void release( ofstream & os ) { 186 unlock( os.lock$ ); 187 } // release 188 189 void ?{}( osacquire & acq, ofstream & os ) { &acq.os = &os; lock( os.lock$ ); } 190 void ^?{}( osacquire & acq ) { release( acq.os ); } 191 179 192 static ofstream soutFile = { (FILE *)stdout }; 180 193 ofstream & sout = soutFile, & stdout = soutFile; … … 194 207 flush( os ); 195 208 return os; 209 // (ofstream &)(os | '\n'); 210 // setPrt$( os, false ); // turn off 211 // setNL$( os, true ); 212 // flush( os ); 213 // return sepOff( os ); // prepare for next line 196 214 } // nl 197 215 … … 201 219 202 220 // private 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$ ); } 221 void ?{}( ifstream & is, void * file ) { 222 is.file$ = file; 223 is.nlOnOff$ = false; 224 is.acquired$ = false; 225 } // ?{} 212 226 213 227 // public 214 228 void ?{}( ifstream & is ) { is.file$ = 0p; } 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$) ); } 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 } // ^?{} 221 241 222 242 void nlOn( ifstream & os ) { os.nlOnOff$ = true; } 223 243 void nlOff( ifstream & os ) { os.nlOnOff$ = false; } 224 225 void ends( ifstream & is ) {} 226 227 bool eof( ifstream & is ) { return feof( (FILE *)(is.file$) ) != 0; } 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 228 261 229 262 void open( ifstream & is, const char name[], const char mode[] ) { 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 263 FILE * file = fopen( name, mode ); 264 #ifdef __CFA_DEBUG__ 237 265 if ( file == 0p ) { 238 266 throw (Open_Failure){ is }; 239 267 // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno ); 240 268 } // if 241 (is){ file }; // initialize 269 #endif // __CFA_DEBUG__ 270 is.file$ = file; 242 271 } // open 243 272 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 ) { 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 ) { 258 282 throw (Close_Failure){ is }; 259 283 // abort | IO_MSG "close input" | nl | strerror( errno ); 260 284 } // if 261 file$ = 0p; // safety after close285 is.file$ = 0p; 262 286 } // close 263 287 … … 288 312 int fmt( ifstream & is, const char format[], ... ) { 289 313 va_list args; 314 290 315 va_start( args, format ); 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 316 int len = vfscanf( (FILE *)(is.file$), format, args ); 298 317 if ( len == EOF ) { 299 318 if ( ferror( (FILE *)(is.file$) ) ) { … … 305 324 } // fmt 306 325 326 inline void acquire( ifstream & is ) { 327 lock( is.lock$ ); 328 if ( ! is.acquired$ ) is.acquired$ = true; 329 else unlock( is.lock$ ); 330 } // acquire 331 332 inline void release( ifstream & is ) { 333 unlock( is.lock$ ); 334 } // release 335 336 void ?{}( isacquire & acq, ifstream & is ) { &acq.is = &is; lock( is.lock$ ); } 337 void ^?{}( isacquire & acq ) { release( acq.is ); } 338 307 339 static ifstream sinFile = { (FILE *)stdin }; 308 340 ifstream & sin = sinFile, & stdin = sinFile; … … 315 347 316 348 // exception I/O constructors 317 void ?{}( Open_Failure & ex, ofstream & ostream ) with(ex) {318 virtual_table = &Open_Failure_vt;319 ostream = &ostream;320 t ag = 1;321 } // ?{} 322 323 void ?{}( Open_Failure & ex, ifstream & istream ) with(ex) {324 virtual_table = &Open_Failure_vt;325 istream = &istream;326 t ag = 0;349 void ?{}( Open_Failure & this, ofstream & ostream ) { 350 this.virtual_table = &Open_Failure_vt; 351 this.ostream = &ostream; 352 this.tag = 1; 353 } // ?{} 354 355 void ?{}( Open_Failure & this, ifstream & istream ) { 356 this.virtual_table = &Open_Failure_vt; 357 this.istream = &istream; 358 this.tag = 0; 327 359 } // ?{} 328 360 … … 331 363 332 364 // exception I/O constructors 333 void ?{}( Close_Failure & ex, ofstream & ostream ) with(ex) {334 virtual_table = &Close_Failure_vt;335 ostream = &ostream;336 t ag = 1;337 } // ?{} 338 339 void ?{}( Close_Failure & ex, ifstream & istream ) with(ex) {340 virtual_table = &Close_Failure_vt;341 istream = &istream;342 t ag = 0;365 void ?{}( Close_Failure & this, ofstream & ostream ) { 366 this.virtual_table = &Close_Failure_vt; 367 this.ostream = &ostream; 368 this.tag = 1; 369 } // ?{} 370 371 void ?{}( Close_Failure & this, ifstream & istream ) { 372 this.virtual_table = &Close_Failure_vt; 373 this.istream = &istream; 374 this.tag = 0; 343 375 } // ?{} 344 376 … … 347 379 348 380 // exception I/O constructors 349 void ?{}( Write_Failure & ex, ofstream & ostream ) with(ex) {350 virtual_table = &Write_Failure_vt;351 ostream = &ostream;352 t ag = 1;353 } // ?{} 354 355 void ?{}( Write_Failure & ex, ifstream & istream ) with(ex) {356 virtual_table = &Write_Failure_vt;357 istream = &istream;358 t ag = 0;381 void ?{}( Write_Failure & this, ofstream & ostream ) { 382 this.virtual_table = &Write_Failure_vt; 383 this.ostream = &ostream; 384 this.tag = 1; 385 } // ?{} 386 387 void ?{}( Write_Failure & this, ifstream & istream ) { 388 this.virtual_table = &Write_Failure_vt; 389 this.istream = &istream; 390 this.tag = 0; 359 391 } // ?{} 360 392 … … 363 395 364 396 // exception I/O constructors 365 void ?{}( Read_Failure & ex, ofstream & ostream ) with(ex) {366 virtual_table = &Read_Failure_vt;367 ostream = &ostream;368 t ag = 1;369 } // ?{} 370 371 void ?{}( Read_Failure & ex, ifstream & istream ) with(ex) {372 virtual_table = &Read_Failure_vt;373 istream = &istream;374 t ag = 0;397 void ?{}( Read_Failure & this, ofstream & ostream ) { 398 this.virtual_table = &Read_Failure_vt; 399 this.ostream = &ostream; 400 this.tag = 1; 401 } // ?{} 402 403 void ?{}( Read_Failure & this, ifstream & istream ) { 404 this.virtual_table = &Read_Failure_vt; 405 this.istream = &istream; 406 this.tag = 0; 375 407 } // ?{} 376 408 -
libcfa/src/fstream.hfa
rb7fd9daf rf95634e 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Oct 10 09:37:32202113 // Update Count : 2 4312 // Last Modified On : Wed Jul 28 07:35:50 2021 13 // Update Count : 234 14 14 // 15 15 … … 36 36 char tupleSeparator$[ofstream_sepSize]; 37 37 multiple_acquisition_lock lock$; 38 bool acquired$; 38 39 }; // ofstream 39 40 … … 52 53 void setPrt$( ofstream &, bool ); 53 54 54 void lock( ofstream & );55 void unlock( ofstream & );56 57 55 // public 58 56 void sepOn( ofstream & ); … … 77 75 void open( ofstream &, const char name[] ); 78 76 void close( ofstream & ); 79 80 77 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 ); 81 87 82 88 void ?{}( ofstream & ); … … 101 107 bool nlOnOff$; 102 108 multiple_acquisition_lock lock$; 109 bool acquired$; 103 110 }; // ifstream 104 111 105 112 // Satisfies istream 106 107 // private108 bool getANL$( ifstream & );109 110 void lock( ifstream & );111 void unlock( ifstream & );112 113 113 114 // public 114 115 void nlOn( ifstream & ); 115 116 void nlOff( ifstream & ); 117 bool getANL( ifstream & ); 116 118 void ends( ifstream & ); 117 119 int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); … … 123 125 void open( ifstream & is, const char name[] ); 124 126 void close( ifstream & is ); 125 126 127 ifstream & read( ifstream & is, char data[], size_t size ); 127 128 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 ); 128 138 129 139 void ?{}( ifstream & is ); -
libcfa/src/heap.cfa
rb7fd9daf rf95634e 102 102 } // prtUnfreed 103 103 104 extern int cfa_main_returned; // from interpose.cfa104 extern int cfa_main_returned; // from bootloader.cf 105 105 extern "C" { 106 106 void heapAppStart() { // called by __cfaabi_appready_startup -
libcfa/src/interpose.cfa
rb7fd9daf rf95634e 94 94 } __cabi_libc; 95 95 96 int cfa_main_returned;97 98 96 extern "C" { 99 97 void __cfaabi_interpose_startup( void ) { 100 98 const char *version = 0p; 101 cfa_main_returned = 0;102 99 103 100 preload_libgcc(); -
libcfa/src/iostream.cfa
rb7fd9daf rf95634e 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : S un Oct 10 09:28:17202113 // Update Count : 134 512 // Last Modified On : Sat May 15 09:39:21 2021 13 // Update Count : 1342 14 14 // 15 15 … … 398 398 return os; 399 399 } // nlOff 400 } // distribution 401 402 forall( ostype & | ostream( ostype ) ) { 403 ostype & acquire( ostype & os ) { 404 acquire( os ); // call void returning 405 return os; 406 } // acquire 400 407 } // distribution 401 408 … … 822 829 fmt( is, "%c", &temp ); // must pass pointer through varg to fmt 823 830 // do not overwrite parameter with newline unless appropriate 824 if ( temp != '\n' || getANL $( is ) ) { c = temp; break; }831 if ( temp != '\n' || getANL( is ) ) { c = temp; break; } 825 832 if ( eof( is ) ) break; 826 833 } // for … … 1028 1035 return is; 1029 1036 } // nlOff 1037 } // distribution 1038 1039 forall( istype & | istream( istype ) ) { 1040 istype & acquire( istype & is ) { 1041 acquire( is ); // call void returning 1042 return is; 1043 } // acquire 1030 1044 } // distribution 1031 1045 -
libcfa/src/iostream.hfa
rb7fd9daf rf95634e 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Oct 10 10:02:07202113 // Update Count : 40 712 // Last Modified On : Wed Apr 28 20:37:56 2021 13 // Update Count : 401 14 14 // 15 15 … … 58 58 void close( ostype & ); 59 59 ostype & write( ostype &, const char [], size_t ); 60 void acquire( ostype & ); // concurrent access 60 61 }; // ostream 61 62 … … 141 142 ostype & nlOn( ostype & ); 142 143 ostype & nlOff( ostype & ); 144 } // distribution 145 146 forall( ostype & | ostream( ostype ) ) { 147 ostype & acquire( ostype & ); 143 148 } // distribution 144 149 … … 291 296 292 297 trait basic_istream( istype & ) { 293 // private 294 bool getANL$( istype & ); // get scan newline (on/off) 295 // public 298 bool getANL( istype & ); // get scan newline (on/off) 296 299 void nlOn( istype & ); // read newline 297 300 void nlOff( istype & ); // scan newline 301 298 302 void ends( istype & os ); // end of output statement 299 303 int fmt( istype &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); … … 308 312 void close( istype & is ); 309 313 istype & read( istype &, char [], size_t ); 314 void acquire( istype & ); // concurrent access 310 315 }; // istream 311 316 … … 374 379 } // distribution 375 380 381 forall( istype & | istream( istype ) ) { 382 istype & acquire( istype & ); 383 } // distribution 384 376 385 // *********************************** manipulators *********************************** 377 386 -
libcfa/src/memory.cfa
rb7fd9daf rf95634e 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 &)164 157 int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that) { 165 158 return this.data == that.data; -
libcfa/src/memory.hfa
rb7fd9daf rf95634e 94 94 95 95 forall(T &) 96 T * release(unique_ptr(T) & this);97 98 forall(T &)99 96 int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that); 100 97 forall(T &) -
libcfa/src/strstream.cfa
rb7fd9daf rf95634e 10 10 // Created On : Thu Apr 22 22:24:35 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Oct 10 16:13:20202113 // Update Count : 10112 // Last Modified On : Tue Apr 27 20:59:53 2021 13 // Update Count : 78 14 14 // 15 15 16 16 #include "strstream.hfa" 17 #include "fstream.hfa" // abort18 17 19 18 #include <stdio.h> // vsnprintf … … 31 30 32 31 // private 33 inlinebool sepPrt$( ostrstream & os ) { setNL$( os, false ); return os.sepOnOff$; }34 inlinevoid sepReset$( ostrstream & os ) { os.sepOnOff$ = os.sepDefault$; }35 inlinevoid sepReset$( ostrstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }36 inlineconst char * sepGetCur$( ostrstream & os ) { return os.sepCur$; }37 inlinevoid sepSetCur$( ostrstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }38 inlinebool getNL$( ostrstream & os ) { return os.sawNL$; }39 inlinevoid setNL$( ostrstream & os, bool state ) { os.sawNL$ = state; }40 inlinebool getANL$( ostrstream & os ) { return os.nlOnOff$; }41 inlinebool getPrt$( ostrstream & os ) { return os.prt$; }42 inlinevoid setPrt$( ostrstream & os, bool state ) { os.prt$ = state; }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; } 43 42 44 43 // public … … 129 128 // *********************************** istrstream *********************************** 130 129 131 // private132 bool getANL$( istrstream & is ) { return is.nlOnOff$; }133 130 134 131 // public … … 139 136 } // ?{} 140 137 138 bool getANL( istrstream & is ) { return is.nlOnOff$; } 141 139 void nlOn( istrstream & is ) { is.nlOnOff$ = true; } 142 140 void nlOff( istrstream & is ) { is.nlOnOff$ = false; } 143 141 144 void ends( istrstream & is ) { }145 bool eof( istrstream & is ) { return false; } 142 void ends( istrstream & is ) { 143 } // ends 146 144 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 145 int eof( istrstream & is ) { 146 return 0; 147 } // eof 164 148 165 149 istrstream &ungetc( istrstream & is, char c ) { … … 170 154 } // ungetc 171 155 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 } // if 166 is.cursor$ += len; 167 return len; 168 } // fmt 169 172 170 // Local Variables: // 173 171 // tab-width: 4 // -
libcfa/src/strstream.hfa
rb7fd9daf rf95634e 10 10 // Created On : Thu Apr 22 22:20:59 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Oct 10 10:14:22202113 // Update Count : 4 712 // Last Modified On : Tue Apr 27 20:58:50 2021 13 // Update Count : 41 14 14 // 15 15 … … 85 85 // Satisfies basic_istream 86 86 87 // private88 bool getANL$( istrstream & );89 90 87 // public 88 bool getANL( istrstream & ); 91 89 void nlOn( istrstream & ); 92 90 void nlOff( istrstream & ); 93 91 void ends( istrstream & ); 92 int fmt( istrstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 93 istrstream & ungetc( istrstream & is, char c ); 94 int eof( istrstream & is ); 94 95 95 int fmt( istrstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) )); 96 istrstream & ungetc( istrstream &, char ); 97 bool eof( istrstream & ); 98 99 void ?{}( istrstream &, char buf[] ); 96 void ?{}( istrstream & is, char buf[] ); 100 97 101 98 // Local Variables: // -
src/AST/Decl.hpp
rb7fd9daf rf95634e 131 131 // declared type, derived from parameter declarations 132 132 ptr<FunctionType> type; 133 /// Null for the forward declaration of a function.134 133 ptr<CompoundStmt> stmts; 135 134 std::vector< ptr<Expr> > withExprs; -
src/AST/Pass.hpp
rb7fd9daf rf95634e 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.351 350 template< typename T > 352 T&GuardValue( T& val ) {351 void GuardValue( T& val ) { 353 352 at_cleanup( [ val ]( void * newVal ) { 354 353 * static_cast< T * >( newVal ) = val; 355 354 }, static_cast< void * >( & val ) ); 356 return val;357 355 } 358 356 … … 396 394 }; 397 395 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 408 396 } 409 397 -
src/AST/Pass.impl.hpp
rb7fd9daf rf95634e 420 420 template< typename core_t > 421 421 inline void ast::accept_all( ast::TranslationUnit & unit, ast::Pass< core_t > & 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 } 422 return ast::accept_all( unit.decls, visitor ); 429 423 } 430 424 -
src/AST/Pass.proto.hpp
rb7fd9daf rf95634e 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 442 428 template<typename core_t> 443 429 static inline auto get_result( core_t & core, char ) -> decltype( core.result() ) { -
src/AST/Stmt.hpp
rb7fd9daf rf95634e 175 175 class CaseStmt final : public Stmt { 176 176 public: 177 /// Null for the default label.178 177 ptr<Expr> cond; 179 178 std::vector<ptr<Stmt>> stmts; -
src/AST/TranslationUnit.hpp
rb7fd9daf rf95634e 26 26 std::list< ptr< Decl > > decls; 27 27 28 struct Global {28 struct Globals { 29 29 std::map< UniqueId, Decl * > idMap; 30 30 31 ptr<Type>sizeType;31 const Type * sizeType; 32 32 const FunctionDecl * dereference; 33 33 const StructDecl * dtorStruct; -
src/AST/porting.md
rb7fd9daf rf95634e 98 98 * `Initializer` => `ast::Init` 99 99 * `Statement` => `ast::Stmt` 100 * `ReferenceToType` => `ast::BaseInstType`101 100 * any field names should follow a similar renaming 102 101 * because they don't really belong to `Type` (and for consistency with `Linkage::Spec`): -
src/CodeGen/FixMain.cc
rb7fd9daf rf95634e 22 22 #include <string> // for operator<< 23 23 24 #include "AST/Decl.hpp"25 #include "AST/Type.hpp"26 #include "Common/PassVisitor.h"27 24 #include "Common/SemanticError.h" // for SemanticError 28 25 #include "CodeGen/GenType.h" // for GenType … … 32 29 33 30 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 52 31 bool FixMain::replace_main = false; 32 std::unique_ptr<FunctionDecl> FixMain::main_signature = nullptr; 53 33 54 34 template<typename container> … … 57 37 } 58 38 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; 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 } 64 46 47 void FixMain::fix(std::ostream &os, const char* bootloader_filename) { 65 48 if( main_signature ) { 66 49 os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return "; 67 main_signature->mangleName = SymTab::Mangler::mangle(main_signature );50 main_signature->mangleName = SymTab::Mangler::mangle(main_signature.get()); 68 51 69 52 os << main_signature->get_scopedMangleName() << "("; … … 82 65 } 83 66 } 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 } // namespace138 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 153 67 }; -
src/CodeGen/FixMain.h
rb7fd9daf rf95634e 9 9 // Author : Thierry Delisle 10 10 // Created On : Thr Jan 12 14:11:09 2017 11 // Last Modified By : Andrew Beach12 // Last Modified On : Fri Oct 29 16:20:00 202113 // Update Count : 811 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 03:24:32 2020 13 // Update Count : 5 14 14 // 15 15 … … 18 18 #include <iosfwd> 19 19 #include <memory> 20 #include <list>21 20 22 21 #include "SynTree/LinkageSpec.h" 23 22 24 class Declaration;25 23 class FunctionDecl; 26 namespace ast {27 class FunctionDecl;28 }29 24 30 25 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 } 31 35 32 class FixMain { 33 public : 34 static inline LinkageSpec::Spec mainLinkage() { 35 return replace_main ? LinkageSpec::Cforall : LinkageSpec::C; 36 } 36 static void registerMain(FunctionDecl* val); 37 37 38 static inline void setReplaceMain(bool val) { 39 replace_main = val; 40 } 38 static void fix(std::ostream &os, const char* bootloader_filename); 41 39 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 40 private: 41 static bool replace_main; 42 static std::unique_ptr<FunctionDecl> main_signature; 43 }; 52 44 } // namespace CodeGen -
src/CodeGen/FixNames.cc
rb7fd9daf rf95634e 9 9 // Author : Richard C. Bilson 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Fri Oct 29 15:49:00 202113 // Update Count : 2 311 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Dec 13 23:39:14 2019 13 // Update Count : 21 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"24 21 #include "Common/PassVisitor.h" 25 22 #include "Common/SemanticError.h" // for SemanticError … … 49 46 }; 50 47 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 51 97 void fixNames( std::list< Declaration* > & translationUnit ) { 52 98 PassVisitor<FixNames> fixer; … … 72 118 fixDWT( functionDecl ); 73 119 74 if ( FixMain::isMain( functionDecl )) {120 if(is_main( SymTab::Mangler::mangle(functionDecl, true, true) )) { 75 121 int nargs = functionDecl->get_functionType()->get_parameters().size(); 76 122 if( !(nargs == 0 || nargs == 2 || nargs == 3) ) { … … 78 124 } 79 125 functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( new ConstantExpr( Constant::from_int( 0 ) ) ) ); 126 CodeGen::FixMain::registerMain( functionDecl ); 80 127 } 81 128 } … … 85 132 GuardAction( [this](){ scopeLevel--; } ); 86 133 } 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_mangled94 && 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 139 134 } // namespace CodeGen 140 135 -
src/CodeGen/FixNames.h
rb7fd9daf rf95634e 9 9 // Author : Richard C. Bilson 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Tue Oct 26 13:47:00 202113 // Update Count : 411 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jul 21 22:17:33 2017 13 // Update Count : 3 14 14 // 15 15 … … 19 19 20 20 class Declaration; 21 namespace ast {22 struct TranslationUnit;23 }24 21 25 22 namespace CodeGen { 26 23 /// mangles object and function names 27 24 void fixNames( std::list< Declaration* > & translationUnit ); 28 void fixNames( ast::TranslationUnit & translationUnit );29 25 } // namespace CodeGen 30 26 -
src/CodeTools/DeclStats.cc
rb7fd9daf rf95634e 156 156 /// number of counting bins for linkages 157 157 static const unsigned n_named_specs = 8; 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 } 158 /// map from total number of specs to bins 159 static const unsigned ind_for_linkage[16]; 171 160 172 161 Stats for_linkage[n_named_specs]; ///< Stores separate stats per linkage … … 377 366 const std::string& mangleName = decl->get_mangleName().empty() ? decl->name : decl->get_mangleName(); 378 367 if ( seen_names.insert( mangleName ).second ) { 379 Stats& stats = for_linkage[ linkage_index( decl->linkage )];368 Stats& stats = for_linkage[ ind_for_linkage[ decl->linkage ] ]; 380 369 381 370 ++stats.n_decls; … … 538 527 }; 539 528 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 540 532 void printDeclStats( std::list< Declaration * > &translationUnit ) { 541 533 PassVisitor<DeclStats> stats; -
src/Common/module.mk
rb7fd9daf rf95634e 22 22 Common/CompilerError.h \ 23 23 Common/Debug.h \ 24 Common/DeclStats.hpp \25 Common/DeclStats.cpp \26 24 Common/ErrorObjects.h \ 27 25 Common/Eval.cc \ … … 35 33 Common/PassVisitor.proto.h \ 36 34 Common/PersistentMap.h \ 37 Common/ResolvProtoDump.hpp \38 Common/ResolvProtoDump.cpp \39 35 Common/ScopedMap.h \ 40 36 Common/SemanticError.cc \ -
src/Concurrency/Keywords.cc
rb7fd9daf rf95634e 93 93 ObjectDecl * addField( StructDecl * ); 94 94 void addRoutines( ObjectDecl *, FunctionDecl * ); 95 void addLockUnlockRoutines( StructDecl * );96 95 97 96 virtual bool is_target( StructDecl * decl ) = 0; … … 323 322 StructDecl* dtor_guard_decl = nullptr; 324 323 StructDecl* thread_guard_decl = nullptr; 325 StructDecl* lock_guard_decl = nullptr;326 324 327 325 static std::unique_ptr< Type > generic_func; … … 465 463 } 466 464 465 467 466 void ConcurrentSueKeyword::handle( StructDecl * decl ) { 468 467 if( ! decl->body ) return; … … 480 479 FunctionDecl * func = forwardDeclare( decl ); 481 480 ObjectDecl * field = addField( decl ); 482 483 // add get_.* routine484 481 addRoutines( field, func ); 485 // add lock/unlock routines to monitors for use by mutex stmt486 addLockUnlockRoutines( decl );487 482 } 488 483 … … 617 612 } 618 613 619 // This function adds the get_.* routine body for coroutines, monitors etc620 // after their corresponding struct has been made621 614 void ConcurrentSueKeyword::addRoutines( ObjectDecl * field, FunctionDecl * func ) { 622 615 CompoundStmt * statement = new CompoundStmt(); … … 641 634 642 635 declsToAddAfter.push_back( get_decl ); 643 }644 645 // Generates lock/unlock routines for monitors to be used by mutex stmts646 void ConcurrentSueKeyword::addLockUnlockRoutines( StructDecl * decl ) {647 // this routine will be called for all ConcurrentSueKeyword children so only continue if we are a monitor648 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 routines654 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 decl664 )665 ),666 nullptr667 );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 monitors679 /*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::Inline692 );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_lock706 }707 )708 )709 );710 lock_decl->set_statements( lock_statement );711 712 //////////////////////////////////////////////////////////////////713 // The following generates this routine for all monitors714 /*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::Inline727 );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_unlock742 }743 )744 )745 );746 unlock_decl->set_statements( unlock_statement );747 748 // pushes routines to declsToAddAfter to add at a later time749 declsToAddAfter.push_back( lock_decl );750 declsToAddAfter.push_back( unlock_decl );751 636 } 752 637 … … 1052 937 assert( !thread_guard_decl ); 1053 938 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;1058 939 } 1059 940 } … … 1200 1081 new PointerType( 1201 1082 noQualifiers, 1202 //new TypeofType( noQualifiers, args.front()->clone() ) 1203 new TypeofType( noQualifiers, new UntypedExpr( 1204 new NameExpr( "__get_type" ), 1205 { args.front()->clone() } 1206 ) 1083 new StructInstType( 1084 noQualifiers, 1085 monitor_decl 1207 1086 ) 1208 1087 ), … … 1214 1093 map_range < std::list<Initializer*> > ( args, [](Expression * var ){ 1215 1094 return new SingleInit( new UntypedExpr( 1216 new NameExpr( "__get_ptr" ),1217 { var }1095 new NameExpr( "get_monitor" ), 1096 { var } 1218 1097 ) ); 1219 //return new SingleInit( new AddressExpr( var ) );1220 1098 }) 1221 1099 ) 1222 1100 ); 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 ) ;1234 1101 1235 1102 // in reverse order : … … 1241 1108 LinkageSpec::Cforall, 1242 1109 nullptr, 1243 lock_guard_struct, 1110 new StructInstType( 1111 noQualifiers, 1112 guard_decl 1113 ), 1244 1114 new ListInit( 1245 1115 { -
src/ControlStruct/ExceptTranslate.cc
rb7fd9daf rf95634e 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // Except Translate.cc -- Conversion of exception control flow structures.7 // ExceptVisitor.cc -- 8 8 // 9 9 // Author : Andrew Beach -
src/ControlStruct/ExceptTranslate.h
rb7fd9daf rf95634e 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // ExceptTranslate.h -- Conversion of exception control flow structures.7 // ExceptTranslate.h -- 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 : Mon Nov 8 11:43:00 202013 // Update Count : 612 // Last Modified On : Tus May 19 11:47:00 2020 13 // Update Count : 5 14 14 // 15 15 … … 19 19 20 20 class Declaration; 21 namespace ast {22 class TranslationUnit;23 }24 21 25 22 namespace ControlStruct { 26 23 void translateThrows( std::list< Declaration *> & translationUnit ); 27 void translateThrows( ast::TranslationUnit & transUnit );28 24 /* Replaces all throw & throwResume statements with function calls. 29 25 * These still need to be resolved, so call this before the reslover. -
src/ControlStruct/LabelGenerator.cc
rb7fd9daf rf95634e 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 : Mon Nov 8 10:18:00 202113 // Update Count : 1 711 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Mar 11 22:23:20 2019 13 // Update Count : 15 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"25 21 #include "SynTree/Attribute.h" // for Attribute 26 22 #include "SynTree/Label.h" // for Label, operator<< … … 28 24 29 25 namespace ControlStruct { 30 31 int LabelGenerator::current = 0; 32 LabelGenerator * LabelGenerator::labelGenerator = nullptr; 26 LabelGenerator * LabelGenerator::labelGenerator = 0; 33 27 34 28 LabelGenerator * LabelGenerator::getGenerator() { … … 49 43 return l; 50 44 } 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 66 45 } // namespace ControlStruct 67 46 -
src/ControlStruct/LabelGenerator.h
rb7fd9daf rf95634e 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 : Mon Nov 8 10:16:00 202113 // Update Count : 811 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Jul 22 09:20:14 2017 13 // Update Count : 6 14 14 // 15 15 … … 21 21 22 22 class Statement; 23 namespace ast {24 class Stmt;25 class Label;26 }27 23 28 24 namespace ControlStruct { 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 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 }; 43 37 } // namespace ControlStruct 44 38 -
src/ControlStruct/module.mk
rb7fd9daf rf95634e 18 18 ControlStruct/ExceptDecl.cc \ 19 19 ControlStruct/ExceptDecl.h \ 20 ControlStruct/FixLabels.cpp \21 ControlStruct/FixLabels.hpp \22 20 ControlStruct/ForExprMutator.cc \ 23 21 ControlStruct/ForExprMutator.h \ … … 28 26 ControlStruct/MLEMutator.cc \ 29 27 ControlStruct/MLEMutator.h \ 30 ControlStruct/MultiLevelExit.cpp \31 ControlStruct/MultiLevelExit.hpp \32 28 ControlStruct/Mutate.cc \ 33 29 ControlStruct/Mutate.h 34 30 35 SRC += $(SRC_CONTROLSTRUCT) \ 36 ControlStruct/ExceptTranslateNew.cpp \ 37 ControlStruct/ExceptTranslate.cc \ 38 ControlStruct/ExceptTranslate.h 39 31 SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc ControlStruct/ExceptTranslate.h 40 32 SRCDEMANGLE += $(SRC_CONTROLSTRUCT) 41 33 -
src/InitTweak/GenInit.cc
rb7fd9daf rf95634e 9 9 // Author : Rob Schluntz 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Mon Oct 25 13:53:00 202113 // Update Count : 18 611 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Dec 13 23:15:10 2019 13 // Update Count : 184 14 14 // 15 15 #include "GenInit.h" … … 24 24 #include "AST/Decl.hpp" 25 25 #include "AST/Init.hpp" 26 #include "AST/Pass.hpp"27 26 #include "AST/Node.hpp" 28 27 #include "AST/Stmt.hpp" … … 295 294 } 296 295 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->dimension355 )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 return391 // 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 } // namespace420 421 void genInit( ast::TranslationUnit & transUnit ) {422 ast::Pass<HoistArrayDimension_NoResolve_New>::run( transUnit );423 ast::Pass<ReturnFixer_New>::run( transUnit );424 }425 426 296 void CtorDtor::generateCtorDtor( std::list< Declaration * > & translationUnit ) { 427 297 PassVisitor<CtorDtor> ctordtor; -
src/InitTweak/GenInit.h
rb7fd9daf rf95634e 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 : Fri Oct 22 16:08:00 202113 // Update Count : 611 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Jul 22 09:31:19 2017 13 // Update Count : 4 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 );30 29 31 30 /// Converts return statements into copy constructor calls on the hidden return variable -
src/MakeLibCfa.h
rb7fd9daf rf95634e 19 19 20 20 class Declaration; 21 namespace ast {22 struct TranslationUnit;23 }24 21 25 22 namespace LibCfa { 26 23 void makeLibCfa( std::list< Declaration* > &prelude ); 27 void makeLibCfa( ast::TranslationUnit & translationUnit );28 24 } // namespace LibCfa 29 25 -
src/Makefile.am
rb7fd9daf rf95634e 23 23 CompilationState.h \ 24 24 MakeLibCfa.cc \ 25 MakeLibCfaNew.cpp \26 25 MakeLibCfa.h 27 26 -
src/Parser/parser.yy
rb7fd9daf rf95634e 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Oct 15 09:20:17202113 // Update Count : 5 16312 // Last Modified On : Tue Jul 20 22:03:04 2021 13 // Update Count : 5031 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 // e.g., u"a" U"b" L"c" is disallowed.71 // i.e., 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 ) );188 187 return new ForCtrl( 189 188 distAttr( DeclarationNode::newTypeof( type, true ), DeclarationNode::newName( index )->addInitializer( new InitializerNode( start ) ) ), … … 310 309 %token ATassign // @= 311 310 312 %type<tok> identifier identifier_at identifier_or_type_name attr_name 311 %type<tok> identifier 312 %type<tok> 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 _list argument_expressiondefault_initializer_opt328 %type<en> argument_expression_list_opt 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 identifier564 560 | '@' // CFA 565 561 { Token tok = { new string( DeclarationNode::anonymous.newName() ), yylval.tok.loc }; $$ = tok; } … … 696 692 // empty 697 693 { $$ = nullptr; } 698 | argument_expression_list 699 ; 700 701 argument_expression_list: 702 argument_expression 694 | argument_expression 703 695 | argument_expression_list_opt ',' argument_expression 704 696 { $$ = (ExpressionNode *)($1->set_last( $3 )); } … … 738 730 | FLOATINGconstant fraction_constants_opt 739 731 { $$ = new ExpressionNode( build_field_name_fraction_constants( build_field_name_FLOATINGconstant( *$1 ), $2 ) ); } 740 | identifier _at fraction_constants_opt // CFA, allow anonymous fields732 | identifier fraction_constants_opt 741 733 { 742 734 $$ = new ExpressionNode( build_field_name_fraction_constants( build_varref( $1 ), $2 ) ); … … 1091 1083 comma_expression_opt ';' 1092 1084 { $$ = 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; }1096 1085 ; 1097 1086 … … 1192 1181 1193 1182 iteration_statement: 1194 WHILE '(' ')' statement // CFA => while ( 1 ) 1183 WHILE '(' push if_control_expression ')' statement pop 1184 { $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 ) ) ); } 1185 | WHILE '(' ')' statement // CFA => while ( 1 ) 1195 1186 { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); } 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; } 1187 | DO statement WHILE '(' comma_expression ')' ';' 1188 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); } 1200 1189 | DO statement WHILE '(' ')' ';' // CFA => do while( 1 ) 1201 1190 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); } 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; } 1191 | FOR '(' push for_control_expression_list ')' statement pop 1192 { $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 ) ) ); } 1206 1193 | FOR '(' ')' statement // CFA => for ( ;; ) 1207 1194 { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); } 1208 | FOR '(' for_control_expression_list ')' statement %prec THEN1209 { $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ) ) ); }1210 | FOR '(' for_control_expression_list ')' statement ELSE statement // CFA1211 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }1212 1195 ; 1213 1196 … … 1355 1338 with_statement: 1356 1339 WITH '(' tuple_expression_list ')' statement 1357 { $$ = new StatementNode( build_with( $3, $5 ) ); } 1340 { 1341 $$ = new StatementNode( build_with( $3, $5 ) ); 1342 } 1358 1343 ; 1359 1344 1360 1345 // If MUTEX becomes a general qualifier, there are shift/reduce conflicts, so change syntax to "with mutex". 1361 1346 mutex_statement: 1362 MUTEX '(' argument_expression_list ')' statement1347 MUTEX '(' argument_expression_list_opt ')' statement 1363 1348 { $$ = new StatementNode( build_mutex( $3, $5 ) ); } 1364 1349 ; … … 2460 2445 | simple_assignment_operator initializer { $$ = $1 == OperKinds::Assign ? $2 : $2->set_maybeConstructed( false ); } 2461 2446 | '=' VOID { $$ = new InitializerNode( true ); } 2462 | '{' initializer_list_opt comma_opt '}' { $$ = new InitializerNode( $2, true ); }2463 2447 ; 2464 2448 … … 2474 2458 | designation initializer { $$ = $2->set_designators( $1 ); } 2475 2459 | initializer_list_opt ',' initializer { $$ = (InitializerNode *)( $1->set_last( $3 ) ); } 2476 | initializer_list_opt ',' designation initializer { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); } 2460 | initializer_list_opt ',' designation initializer 2461 { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); } 2477 2462 ; 2478 2463 … … 2489 2474 designation: 2490 2475 designator_list ':' // C99, CFA uses ":" instead of "=" 2491 | identifier _at':' // GCC, field name2476 | identifier ':' // GCC, field name 2492 2477 { $$ = new ExpressionNode( build_varref( $1 ) ); } 2493 2478 ; … … 2501 2486 2502 2487 designator: 2503 '.' identifier _at// C99, field name2488 '.' identifier // C99, field name 2504 2489 { $$ = new ExpressionNode( build_varref( $2 ) ); } 2505 2490 | '[' push assignment_expression pop ']' // C99, single array element … … 2933 2918 2934 2919 paren_identifier: 2935 identifier _at2920 identifier 2936 2921 { $$ = DeclarationNode::newName( $1 ); } 2937 2922 | '(' paren_identifier ')' // redundant parenthesis -
src/ResolvExpr/module.mk
rb7fd9daf rf95634e 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.hpp68 63 64 SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc ResolvExpr/AlternativePrinter.h 69 65 SRCDEMANGLE += $(SRC_RESOLVEXPR) -
src/Tuples/TupleExpansionNew.cpp
rb7fd9daf rf95634e 8 8 // 9 9 // Author : Henry Xue 10 // Created On : Mon Aug 23 15:36:09202110 // Created On : Wed Aug 18 12:54:02 2021 11 11 // Last Modified By : Henry Xue 12 // Last Modified On : Mon Aug 23 15:36:09202112 // Last Modified On : Wed Aug 18 12:54:02 2021 13 13 // Update Count : 1 14 14 // 15 16 // Currently not working due to unresolved issues with UniqueExpr 15 17 16 18 #include "Tuples.h" … … 18 20 namespace Tuples { 19 21 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 };24 22 struct UniqueExprExpander final : public ast::WithDeclsToAdd<> { 25 23 const ast::Expr * postvisit( const ast::UniqueExpr * unqExpr ); 26 24 std::map< int, ast::ptr<ast::Expr> > decls; // not vector, because order added may not be increasing order 27 25 }; 28 } // namespace29 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 precedence38 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 recursively41 // 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 return49 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 expressions60 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 aggregate68 return new ast::UntypedMemberExpr( loc, memberExpr->member, memberExpr->aggregate->accept( *visitor ) );69 }70 }71 26 } // namespace 72 27 -
src/Tuples/Tuples.h
rb7fd9daf rf95634e 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Henry Xue12 // Last Modified On : Mon Aug 23 15:36:09 202113 // Update Count : 1 911 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Jun 18 09:36:00 2019 13 // Update Count : 18 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 );42 41 43 42 /// replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc. -
src/Tuples/module.mk
rb7fd9daf rf95634e 10 10 ## Author : Richard C. Bilson 11 11 ## Created On : Mon Jun 1 17:49:17 2015 12 ## Last Modified By : Henry Xue13 ## Last Modified On : Mon Aug 23 15:36:09 202114 ## Update Count : 212 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Mon Jun 1 17:54:33 2015 14 ## Update Count : 1 15 15 ############################################################################### 16 16 -
src/main.cc
rb7fd9daf rf95634e 9 9 // Author : Peter Buhr and Rob Schluntz 10 10 // Created On : Fri May 15 23:12:02 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Tue Nov 9 11:10:00202113 // Update Count : 65 711 // Last Modified By : Henry Xue 12 // Last Modified On : Tue Jul 20 04:27:35 2021 13 // Update Count : 658 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 printDeclStats46 #include "Common/ResolvProtoDump.hpp" // for dumpAsResolverProto47 45 #include "Common/Stats.h" 48 46 #include "Common/PassVisitor.h" … … 53 51 #include "ControlStruct/ExceptDecl.h" // for translateExcept 54 52 #include "ControlStruct/ExceptTranslate.h" // for translateEHM 55 #include "ControlStruct/FixLabels.hpp" // for fixLabels56 53 #include "ControlStruct/Mutate.h" // for mutate 57 54 #include "GenPoly/Box.h" // for box … … 65 62 #include "Parser/TypedefTable.h" // for TypedefTable 66 63 #include "ResolvExpr/AlternativePrinter.h" // for AlternativePrinter 67 #include "ResolvExpr/CandidatePrinter.hpp" // for printCandidates68 64 #include "ResolvExpr/Resolver.h" // for resolve 69 65 #include "SymTab/Validate.h" // for validate … … 319 315 // add the assignment statement after the initialization of a type parameter 320 316 PASS( "Validate", SymTab::validate( translationUnit, symtabp ) ); 317 if ( symtabp ) { 318 deleteAll( translationUnit ); 319 return EXIT_SUCCESS; 320 } // if 321 322 if ( expraltp ) { 323 PassVisitor<ResolvExpr::AlternativePrinter> printer( cout ); 324 acceptAll( translationUnit, printer ); 325 return EXIT_SUCCESS; 326 } // if 327 328 if ( validp ) { 329 dump( translationUnit ); 330 return EXIT_SUCCESS; 331 } // if 332 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 functions 340 LibCfa::makeLibCfa( translationUnit ); 341 } // if 342 343 if ( declstatsp ) { 344 CodeTools::printDeclStats( translationUnit ); 345 deleteAll( translationUnit ); 346 return EXIT_SUCCESS; 347 } // if 348 349 if ( bresolvep ) { 350 dump( translationUnit ); 351 return EXIT_SUCCESS; 352 } // if 321 353 322 354 CodeTools::fillLocations( translationUnit ); 355 356 if ( resolvprotop ) { 357 CodeTools::dumpAsResolvProto( translationUnit ); 358 return EXIT_SUCCESS; 359 } // if 323 360 324 361 if( useNewAST ) { … … 328 365 } 329 366 auto transUnit = convert( move( translationUnit ) ); 330 331 forceFillCodeLocations( transUnit );332 333 if ( symtabp ) {334 return EXIT_SUCCESS;335 } // if336 337 if ( expraltp ) {338 ResolvExpr::printCandidates( transUnit );339 return EXIT_SUCCESS;340 } // if341 342 if ( validp ) {343 dump( move( transUnit ) );344 return EXIT_SUCCESS;345 } // if346 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 } // if357 358 if ( declstatsp ) {359 printDeclStats( transUnit );360 return EXIT_SUCCESS;361 } // if362 363 if ( bresolvep ) {364 dump( move( transUnit ) );365 return EXIT_SUCCESS;366 } // if367 368 if ( resolvprotop ) {369 dumpAsResolverProto( transUnit );370 return EXIT_SUCCESS;371 } // if372 373 367 PASS( "Resolve", ResolvExpr::resolve( transUnit ) ); 374 368 if ( exprp ) { … … 391 385 translationUnit = convert( move( transUnit ) ); 392 386 } else { 393 if ( symtabp ) {394 deleteAll( translationUnit );395 return EXIT_SUCCESS;396 } // if397 398 if ( expraltp ) {399 PassVisitor<ResolvExpr::AlternativePrinter> printer( cout );400 acceptAll( translationUnit, printer );401 return EXIT_SUCCESS;402 } // if403 404 if ( validp ) {405 dump( translationUnit );406 return EXIT_SUCCESS;407 } // if408 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 } // if419 420 if ( declstatsp ) {421 CodeTools::printDeclStats( translationUnit );422 deleteAll( translationUnit );423 return EXIT_SUCCESS;424 } // if425 426 if ( bresolvep ) {427 dump( translationUnit );428 return EXIT_SUCCESS;429 } // if430 431 CodeTools::fillLocations( translationUnit );432 433 if ( resolvprotop ) {434 CodeTools::dumpAsResolvProto( translationUnit );435 return EXIT_SUCCESS;436 } // if437 438 387 PASS( "Resolve", ResolvExpr::resolve( translationUnit ) ); 439 388 if ( exprp ) { … … 498 447 PASS( "Code Gen", CodeGen::generate( translationUnit, *output, ! genproto, prettycodegenp, true, linemarks ) ); 499 448 500 CodeGen::FixMain::fix( translationUnit, *output, 501 (PreludeDirector + "/bootloader.c").c_str() ); 449 CodeGen::FixMain::fix( *output, (PreludeDirector + "/bootloader.c").c_str() ); 502 450 if ( output != &cout ) { 503 451 delete output; -
tests/.expect/declarationSpecifier.x64.txt
rb7fd9daf rf95634e 1132 1132 char **_X13cfa_args_argvPPc_1; 1133 1133 char **_X13cfa_args_envpPPc_1; 1134 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;1134 signed int _X17cfa_main_returnedi_1 = ((signed int )0); 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 if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) { 1152 { 1153 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 1154 } 1155 1151 { 1152 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 1156 1153 } 1157 1154 -
tests/.expect/declarationSpecifier.x86.txt
rb7fd9daf rf95634e 1132 1132 char **_X13cfa_args_argvPPc_1; 1133 1133 char **_X13cfa_args_envpPPc_1; 1134 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;1134 signed int _X17cfa_main_returnedi_1 = ((signed int )0); 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 if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) { 1152 { 1153 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 1154 } 1155 1151 { 1152 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 1156 1153 } 1157 1154 -
tests/.expect/gccExtensions.x64.txt
rb7fd9daf rf95634e 324 324 char **_X13cfa_args_argvPPc_1; 325 325 char **_X13cfa_args_envpPPc_1; 326 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;326 signed int _X17cfa_main_returnedi_1 = ((signed int )0); 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 if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) { 344 { 345 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 346 } 347 343 { 344 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 348 345 } 349 346 -
tests/.expect/gccExtensions.x86.txt
rb7fd9daf rf95634e 302 302 char **_X13cfa_args_argvPPc_1; 303 303 char **_X13cfa_args_envpPPc_1; 304 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;304 signed int _X17cfa_main_returnedi_1 = ((signed int )0); 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 if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) { 322 { 323 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 324 } 325 321 { 322 ((void)(_X17cfa_main_returnedi_1=((signed int )1))); 326 323 } 327 324 -
tests/Makefile.am
rb7fd9daf rf95634e 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 \80 77 io/.in/io.data \ 81 78 io/.in/many_read.data \ -
tests/concurrent/mutexstmt/locks.cfa
rb7fd9daf rf95634e 1 #include <mutex_stmt .hfa>1 #include <mutex_stmt_locks.hfa> 2 2 #include <locks.hfa> 3 3 4 4 const unsigned int num_times = 10000; 5 5 6 single_acquisition_lock m1, m2, m3, m4, m5;6 owner_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
rb7fd9daf rf95634e 1 1 #include <monitor.hfa> 2 #include <mutex_stmt.hfa>3 2 #include <stdio.h> 4 3 #include <stdlib.hfa> … … 14 13 bool insideFlag = false; 15 14 int count = 0; 16 bool startFlag = false;17 15 18 16 void main( T_Mutex & this ) { -
tests/concurrent/semaphore.cfa
rb7fd9daf rf95634e 2 2 #include <locks.hfa> 3 3 #include <thread.hfa> 4 #include <mutex_stmt.hfa>5 4 6 5 enum { num_blockers = 17, num_unblockers = 13 }; … … 29 28 thrash(); 30 29 P(ben); 31 if(((thread&)this).seqable.next != 0p) mutex(sout) sout |"Link not invalidated";30 if(((thread&)this).seqable.next != 0p) sout | acquire |"Link not invalidated"; 32 31 thrash(); 33 32 } -
tests/concurrent/sleep.cfa
rb7fd9daf rf95634e 1 1 #include <fstream.hfa> 2 2 #include <thread.hfa> 3 #include <mutex_stmt.hfa>4 3 #include <time.hfa> 5 4 … … 30 29 31 30 int main() { 32 mutex( sout ) sout| "start";31 sout | acquire | "start"; 33 32 { 34 33 slow_sleeper slow; … … 37 36 yield(); 38 37 } 39 mutex( sout ) sout| "done";38 sout | acquire | "done"; 40 39 } 41 40 -
tests/exceptions/.expect/type-check.txt
rb7fd9daf rf95634e 1 exceptions/type-check.cfa: 6:1 error: catch must have pointer to an exception type2 exceptions/type-check.cfa: 7:1 error: catch must have pointer to an exception type3 exceptions/type-check.cfa: 8:1 error: catchResume must have pointer to an exception type4 exceptions/type-check.cfa: 9:1 error: catchResume must have pointer to an exception type1 exceptions/type-check.cfa:8:1 error: catch must have pointer to an exception type 2 exceptions/type-check.cfa:9:1 error: catch must have pointer to an exception type 3 exceptions/type-check.cfa:10:1 error: catchResume must have pointer to an exception type 4 exceptions/type-check.cfa:11:1 error: catchResume must have pointer to an exception type -
tests/exceptions/cancel/coroutine.cfa
rb7fd9daf rf95634e 2 2 3 3 #include <coroutine.hfa> 4 #include <exception.hfa> 4 5 5 exception internal_error {};6 vtable(internal_error) internal_vt;6 EHM_EXCEPTION(internal_error)(); 7 EHM_VIRTUAL_TABLE(internal_error, internal_vt); 7 8 8 9 coroutine WillCancel {}; -
tests/exceptions/cancel/thread.cfa
rb7fd9daf rf95634e 2 2 3 3 #include <thread.hfa> 4 #include <exception.hfa> 4 5 5 exception internal_error {};6 vtable(internal_error) internal_vt;6 EHM_EXCEPTION(internal_error)(); 7 EHM_VIRTUAL_TABLE(internal_error, internal_vt); 7 8 8 9 thread WillCancel {}; -
tests/exceptions/conditional.cfa
rb7fd9daf rf95634e 4 4 // up the non-trivial exception is reasonable to do. 5 5 6 exception num_error { 6 #include <exception.hfa> 7 8 EHM_EXCEPTION(num_error)( 7 9 int num; 8 };10 ); 9 11 10 vtable(num_error) num_error_vt;12 EHM_VIRTUAL_TABLE(num_error, num_error_vt); 11 13 12 14 void caught_num_error(int expect, num_error * actual) { -
tests/exceptions/data-except.cfa
rb7fd9daf rf95634e 1 1 // Test exceptions that add data but no functionality. 2 2 3 exception paired { 3 #include <exception.hfa> 4 5 EHM_EXCEPTION(paired)( 4 6 int first; 5 7 int second; 6 };8 ); 7 9 8 vtable(paired) paired_vt;10 EHM_VIRTUAL_TABLE(paired, paired_vt); 9 11 10 12 const char * virtual_msg(paired * this) { -
tests/exceptions/defaults.cfa
rb7fd9daf rf95634e 4 4 #include <exception.hfa> 5 5 6 exception log_message { 6 EHM_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 exception jump {};34 EHM_EXCEPTION(jump)(); 35 35 void defaultTerminationHandler(jump &) { 36 36 printf("jump default handler.\n"); 37 37 } 38 38 39 vtable(jump) jump_vt;39 EHM_VIRTUAL_TABLE(jump, jump_vt); 40 40 41 41 void jump_test(void) { … … 48 48 } 49 49 50 exception first {};51 vtable(first) first_vt;50 EHM_EXCEPTION(first)(); 51 EHM_VIRTUAL_TABLE(first, first_vt); 52 52 53 exception unhandled_exception {};54 vtable(unhandled_exception) unhandled_vt;53 EHM_EXCEPTION(unhandled_exception)(); 54 EHM_VIRTUAL_TABLE(unhandled_exception, unhandled_vt); 55 55 56 56 void unhandled_test(void) { … … 69 69 } 70 70 71 exception second {};72 vtable(second) second_vt;71 EHM_EXCEPTION(second)(); 72 EHM_VIRTUAL_TABLE(second, second_vt); 73 73 74 74 void cross_test(void) { -
tests/exceptions/finally.cfa
rb7fd9daf rf95634e 1 1 // Finally Clause Tests 2 2 3 #include <exception.hfa> 3 4 #include "except-io.hfa" 4 5 5 exception myth {};6 EHM_EXCEPTION(myth)(); 6 7 7 vtable(myth) myth_vt;8 EHM_VIRTUAL_TABLE(myth, myth_vt); 8 9 9 10 int main(int argc, char * argv[]) { -
tests/exceptions/interact.cfa
rb7fd9daf rf95634e 1 1 // Testing Interactions Between Termination and Resumption 2 2 3 #include <exception.hfa> 3 4 #include "except-io.hfa" 4 5 5 exception star {};6 exception moon {};6 EHM_EXCEPTION(star)(); 7 EHM_EXCEPTION(moon)(); 7 8 8 vtable(star) star_vt;9 vtable(moon) moon_vt;9 EHM_VIRTUAL_TABLE(star, star_vt); 10 EHM_VIRTUAL_TABLE(moon, moon_vt); 10 11 11 12 int main(int argc, char * argv[]) { -
tests/exceptions/polymorphic.cfa
rb7fd9daf rf95634e 1 1 // Testing polymophic exception types. 2 2 3 forall(T &) exception proxy {}; 3 #include <exception.hfa> 4 4 5 vtable(proxy(int)) proxy_int; 6 vtable(proxy(char)) proxy_char; 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); 7 9 8 10 void proxy_test(void) { … … 31 33 } 32 34 33 forall(T) exception cell { 35 EHM_FORALL_EXCEPTION(cell, (T), (T))( 34 36 T data; 35 };37 ); 36 38 37 vtable(cell(int)) int_cell;38 vtable(cell(char)) char_cell;39 vtable(cell(bool)) bool_cell;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); 40 42 41 43 void cell_test(void) { -
tests/exceptions/resume.cfa
rb7fd9daf rf95634e 1 1 // Resumption Exception Tests 2 2 3 #include <exception.hfa> 3 4 #include "except-io.hfa" 4 5 5 exception yin {};6 exception yang {};7 exception zen {};6 EHM_EXCEPTION(yin)(); 7 EHM_EXCEPTION(yang)(); 8 EHM_EXCEPTION(zen)(); 8 9 9 vtable(yin) yin_vt;10 vtable(yang) yang_vt;11 vtable(zen) zen_vt;10 EHM_VIRTUAL_TABLE(yin, yin_vt); 11 EHM_VIRTUAL_TABLE(yang, yang_vt); 12 EHM_VIRTUAL_TABLE(zen, zen_vt); 12 13 13 14 void in_void(void); -
tests/exceptions/terminate.cfa
rb7fd9daf rf95634e 1 1 // Termination Exception Tests 2 2 3 #include <exception.hfa> 3 4 #include "except-io.hfa" 4 5 5 exception yin {};6 exception yang {};7 exception zen {};6 EHM_EXCEPTION(yin)(); 7 EHM_EXCEPTION(yang)(); 8 EHM_EXCEPTION(zen)(); 8 9 9 vtable(yin) yin_vt;10 vtable(yang) yang_vt;11 vtable(zen) zen_vt;10 EHM_VIRTUAL_TABLE(yin, yin_vt); 11 EHM_VIRTUAL_TABLE(yang, yang_vt); 12 EHM_VIRTUAL_TABLE(zen, zen_vt); 12 13 13 14 void in_void(void); -
tests/exceptions/trash.cfa
rb7fd9daf rf95634e 1 1 // Make sure throw-catch during unwind does not trash internal data. 2 2 3 exception yin {}; 4 exception yang {}; 3 #include <exception.hfa> 5 4 6 vtable(yin) yin_vt; 7 vtable(yang) yang_vt; 5 EHM_EXCEPTION(yin)(); 6 EHM_EXCEPTION(yang)(); 7 8 EHM_VIRTUAL_TABLE(yin, yin_vt); 9 EHM_VIRTUAL_TABLE(yang, yang_vt); 8 10 9 11 int main(int argc, char * argv[]) { -
tests/exceptions/type-check.cfa
rb7fd9daf rf95634e 1 1 // Check that the exception type check works. 2 2 3 exception truth {}; 3 #include <exception.hfa> 4 5 EHM_EXCEPTION(truth)(); 4 6 5 7 int main(int argc, char * argv[]) { -
tests/io/io-acquire.cfa
rb7fd9daf rf95634e 10 10 // Created On : Mon Mar 1 18:40:09 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Oct 6 18:04:58202113 // Update Count : 7212 // Last Modified On : Tue Apr 27 11:49:34 2021 13 // Update Count : 18 14 14 // 15 15 16 16 #include <fstream.hfa> 17 17 #include <thread.hfa> 18 #include <mutex_stmt.hfa>19 18 20 19 thread T {}; … … 22 21 // output from parallel threads should not be scrambled 23 22 24 for ( 100 ) { // expressionprotection25 mutex(sout) sout| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;23 for ( 100 ) { // local protection 24 sout | acquire | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; 26 25 } 27 mutex( sout ) { // statement protection 26 { // global protection (RAII) 27 osacquire acq = { sout }; 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 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 } 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; 46 38 } 47 39 } … … 50 42 51 43 int a, b, c, d, e, f, g, h, i; 52 for ( 100 ) { // expressionprotection53 mutex(sin) sin| a | b | c | d | e | f | g | h | i;44 for ( 100 ) { // local protection 45 sin | acquire | a | b | c | d | e | f | g | h | i; 54 46 } 55 mutex( sin ) { // statement protection 47 { // global protection (RAII) 48 isacquire acq = { sin }; 56 49 for ( 100 ) { 57 50 sin | a | b | c | d | e | f | g | h | i; … … 59 52 } 60 53 { // duplicate protection demonstrating recursive lock 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 } 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; 74 59 } 75 60 } -
tests/linking/io-acquire.cfa
rb7fd9daf rf95634e 17 17 #include <fstream.hfa> 18 18 #include <stdlib.hfa> 19 #include <mutex_stmt.hfa>20 19 21 20 int main() { 22 21 int i; 23 22 if(threading_enabled()) { 24 mutex( stdout ) stdout| "YES";23 stdout | acquire | "YES"; 25 24 stdin | i; 26 25 } else { 27 mutex( stdout ) stdout| "NO";26 stdout | acquire | "NO"; 28 27 stdin | i; 29 28 } -
tests/pybin/test_run.py
rb7fd9daf rf95634e 65 65 def toString( cls, retcode, duration ): 66 66 if settings.generating : 67 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" % retcode67 if retcode == TestResult.SUCCESS: text = "Done " 68 elif retcode == TestResult.TIMEOUT: text = "TIMEOUT" 69 else : text = "ERROR code %d" % retcode 70 70 else : 71 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" % retcode71 if retcode == TestResult.SUCCESS: text = "PASSED " 72 elif retcode == TestResult.TIMEOUT: text = "TIMEOUT" 73 else : text = "FAILED with code %d" % retcode 74 74 75 75 text += " C%s - R%s" % (fmtDur(duration[0]), fmtDur(duration[1])) 76 return key,text76 return text -
tests/test.py
rb7fd9daf rf95634e 257 257 258 258 # update output based on current action 259 result_ key, result_txt = TestResult.toString( retcode, duration )259 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, result_key,text267 return retcode == TestResult.SUCCESS, text 268 268 except KeyboardInterrupt: 269 return False, 'keybrd',""269 return False, "" 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 = 0287 285 288 286 # for each test to run … … 296 294 ) 297 295 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 296 for i, (succ, txt) in enumerate(timed(results, timeout = settings.timeout.total), 1) : 304 297 if not succ : 305 298 failed = True … … 326 319 # clean the workspace 327 320 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))330 321 331 322 return failed … … 452 443 failed = run_tests(local_tests, options.jobs) 453 444 if failed: 445 result = 1 454 446 if not settings.continue_: 455 447 break -
tools/perf/process_stat_array.py
rb7fd9daf rf95634e 1 1 #!/usr/bin/python3 2 2 3 import argparse, json, math, os, sys, re 4 from PIL import Image 5 import numpy as np 3 import argparse, os, sys, re 6 4 7 5 def dir_path(string): … … 13 11 parser = argparse.ArgumentParser() 14 12 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)16 13 17 14 try : … … 26 23 counters = {} 27 24 28 max_cpu = 029 min_cpu = 100000030 max_tsc = 031 min_tsc = 1844674407370955161532 33 25 #open the files 34 26 for filename in filenames: … … 39 31 with open(os.path.join(root, filename), 'r') as file: 40 32 for line in file: 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]) 33 # data = [int(x.strip()) for x in line.split(',')] 34 data = [int(line.strip())] 35 data = [me, *data] 60 36 merged.append(data) 61 37 62 except Exception as e: 63 print(e) 38 except: 64 39 pass 65 40 66 67 print({"max-cpu": max_cpu, "min-cpu": min_cpu, "max-tsc": max_tsc, "min-tsc": min_tsc})68 41 69 42 # Sort by timestamp (the second element) … … 74 47 merged.sort(key=takeSecond) 75 48 76 json.dump({"values":merged, "max-cpu": max_cpu, "min-cpu": min_cpu, "max-tsc": max_tsc, "min-tsc": min_tsc}, args.out) 49 # for m in merged: 50 # print(m) 77 51 78 # vmin = merged[ 0][1] 79 # vmax = float(merged[-1][1] - vmin) / 2500000000.0 80 # # print(vmax) 52 single = [] 53 curr = 0 81 54 82 # bins = [] 83 # for _ in range(0, int(math.ceil(vmax * 10))): 84 # bins.append([0] * (32 * 32)) 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 85 62 86 # # print(len(bins)) 87 # bins = np.array(bins) 63 # add change to the current 64 curr = curr + change 65 single.append( value ) 88 66 89 # rejected = 0 90 # highest = 0 67 pass 91 68 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) 69 print(single) 143 70 144 71 # single = sorted(single)[:len(single)-100]
Note:
See TracChangeset
for help on using the changeset viewer.